Update 12/10/2008: Don’t use this technique; use the guidance here instead: The New Definitive SPSite/SPWeb Disposal Article
Chris O’Brien’s recent post discusses the need to dispose of SPSite and SPWeb objects, but only if they didn’t come from SPContext. To do this he depends on a list’s ParentWeb property, which currently returns the same instance as the original SPWeb. However, he notes that this strategy depends on the internal implementation of ParentWeb, which (however unlikely) might change.
As a future-proof alternative, I propose capturing the SPWeb as an out
parameter if it will need to be disposed:
public void DoSomething() { SPWeb webToDispose = null; SPList list = getList(out webToDispose); // do something with list.. foreach (SPListItem item in list.Items) { processItem(item); } // ** PROBLEM - how do we now dispose of the SPSite/SPWeb objects we created earlier? ** // ** SOLUTION - if we didn't use context, webToDispose has reference if (webToDispose != null) { webToDispose.Dispose(); webToDispose.Site.Dispose(); } } private SPList getList(out SPWeb webToDispose) { webToDispose = null; SPContext currentContext = SPContext.Current; SPList list = null; if (currentContext != null) { list = currentContext.Site.AllWebs["MyWeb"].Lists["MyList"]; } else { SPSite site = new SPSite("http://solutionizing.net"); webToDispose = site.OpenWeb("/MyWeb"); list = webToDispose.Lists["MyList"]; } return list; }
This code should be functionally equivalent and perhaps a bit easier to read, but without the dependency on ParentWeb. Thoughts?
Also, to add to Chris’s list of required reading, check out Roger Lamb’s excellent SharePoint 2007 and WSS 3.0 Dispose Patterns by Example.
Update 7/23/2008: How ironic…Chris and I have a leak! If you can’t spot it, I explain here.
July 23, 2008 at 1:50 am
[…] Leaks Revisited July 23, 2008 — Keith Dahlby A while back I posted a rather clumsy technique to mitigate an SPWeb leak discussed here. I knew there had to be a better way, and Rob […]
August 10, 2008 at 12:39 am
[…] SPList.ParentWeb a Leak? August 10, 2008 — Keith Dahlby As previously discussed, doing the “right thing” with our SPSite and SPWeb references’ […]
October 30, 2008 at 12:07 pm
SPSite site = new SPSite(“http://solutionizing.net”);
shouldn’t you dispose the site too?
i.e.
using(SPSite site = new SPSite(“http://solutionizing.net”)){
webToDispose = site.OpenWeb(“/MyWeb”);
list = webToDispose.Lists[“MyList”];
}
October 30, 2008 at 12:51 pm
The site is disposed at the end of DoSomething():
if (webToDispose != null) {
webToDispose.Dispose();
webToDispose.Site.Dispose(); // <–
}
The site can’t be disposed within getList because disposing an SPSite also disposes all webs opened from it, including the web we need to leave open.
October 31, 2008 at 7:15 am
I’d not tired executing it! My feeling is that
private SPList getList(out SPWeb webToDispose)
is dangerous though as you need to remember to know what to do with webToDispose perhaps out
I think I’d rather see
private SPList getList(out object[] objectsToDispose)
I’m still thinking that I’d rather see neither
For my task I’m going to try using similar idea to the CacheData function here
http://msdn.microsoft.com/en-us/library/bb687949.aspx
October 31, 2008 at 9:14 am
I agree that this approach isn’t pretty, it was just one of my first investigations into this issue. I prefer the pattern described in my post “SPSite/SPWeb Leaks Revisited”, though the initial example is still rather contrived. Ultimately there are several correct solutions, depending on what you need from the objects in question. Just be careful returning any SharePoint object from a function in which its parent web (or site) has been disposed. If you’re caching a DataTable as recommended, you shouldn’t have much to worry about.
November 1, 2008 at 4:44 am
What I’m still struggling to get my head round is that even if you move the
webToDispose.Dispose();
webToDispose.Site.Dispose();
to inside the getList method; when the foreach on the returned SPList doesn’t fail. What’s more is that if I compare the hash code before my premature dispose and after the for each they are the same, why? I have a few ideas but don’t know how to find out which is right yet.
Secondly if I return a SPListItem or SPListItem.File etc. do I still need to dispose of the SPSite and SPWeb only after I’ve used them?
November 1, 2008 at 11:34 am
You’ve discovered the fundamental issue with the current implementation of Dispose(): The objects are closed and marked as invalid, but they still work. However, behavior of an IDisposable after it has been disposed is technically undefined. While the code might seem to work 99% of the time, there is absolutely no guarantee that it will always work.
Regarding returning an SPListItem or SPFile, I think you have to assume they might need a reference to their parent Web at any time. So you should either extract the data you need into a non-SP object (DataTable, custom business object, anonymous type in 3.5), or find a way to Dispose later. Ultimately, you should only dispose if you’re 100% certain the reference will never be used again.