Official Microsoft guidance is to never explicitly Dispose()
SPWeb.ParentWeb
. I generally agree with this advice, given that my Rule #1 of SharePoint disposal is that “Using a disposed object can cause more problems than failing to dispose.” To understand why, I’ll borrow my explanation from SPDevWiki:
This property will allocate an
SPWeb
object the first time it is called. The caveat is that once it is disposed, any reference to the property will return the disposed object. If anSPWeb
is not owned by the developer, itsParentWeb
should be considered not owned as well. For example, there could be a problem if two components both depend onSPContext.Current.Web.ParentWeb
and one callsDispose()
before the other is done with it.
However, this can result in memory pressure in cases involving enumeration or where the parent SPSite
has a long lifetime. For example:
SPSite contextSite = SPContext.Current.Site; foreach(SPWeb web in contextSite.AllWebs.AsSafeEnumerable()) { SPWeb webParent = web.ParentWeb; // Internal OpenWeb() // Do something with web and webParent }
The web
references are disposed by my safe iterator, but every webParent
will remain open until the context SPSite
is disposed. Not that I would recommend using code like this (in fact I would strongly urge against it), but you can never say never.
To that end, I propose a simple extension method whose contract is clear: always dispose me! We can still follow MS guidance regarding SPWeb.ParentWeb
, but have convenient access to a developer-owned parent SPWeb
as well:
[SPDisposeCheckIgnore(SPDisposeCheckID.SPDisposeCheckID_120, "By Design")] public static SPWeb GetParentWeb(this SPWeb web) { if(web == null) throw new ArgumentNullException("web"); return web.Site.OpenWeb(web.ParentWebId); }
And our “Best Practice” memory pressure can be revised slightly to achieve much better memory use:
SPSite contextSite = SPContext.Current.Site; foreach(SPWeb web in contextSite.AllWebs.AsSafeEnumerable()) { using(SPWeb webParent = web.GetParentWeb()) { // Do something with web and webParent } }
Trivial? Obvious? Perhaps. But often the most useful code is.
Update: Included appropriate SPDisposeCheckIgnore
attribute for “leaked” SPWeb
from OpenWeb()
; we know what we’re doing. That said, you could certainly implement higher-order functions to invoke an action
or selector
against our imitation ParentWeb without returning it—I’ll leave those as an exercise for the reader.
March 19, 2009 at 3:56 pm
In the extension method, when you do web.Site.OpenWeb().. aren’t you leaking Site object?
March 19, 2009 at 6:21 pm
@Raja ~ Nope – a web’s Site property always returns a reference to the SPSite from which it was opened.
Cheers ~
Keith
March 19, 2009 at 6:31 pm
[…] Introducing SPWeb.GetParentWeb() […]
June 1, 2009 at 1:22 am
[…] Introducing SPWeb.GetParentWeb() […]