The New Definitive SPSite/SPWeb Disposal Article

As I’ve posted before, properly cleaning up your SPSite and SPWeb objects is an important yet confusing consideration in writing good SharePoint object model code. There are some decent articles out there already, but Stefan Goßner just posted the best guidance – by far – I’ve seen on the subject: Disposing SPWeb and SPSite objects. Seriously, go read it.

In my view, Stefan hit on four key points that bear repeating:

1. Ensure that you only dispose SPSite and SPWeb objects that your code owns.

Examples that you don’t own, most of which I have seen incorrectly disposed in various examples online:

  • SPContext.Current.Web
  • SPContext.Current.Site
  • SPContext.Current.Site.RootWeb
  • SPFeatureReceiverProperties.Feature.Parent for a Site- or Web-scoped feature receiver
  • SPWebEventProperties.Web
  • SPListEventProperties.Web
  • SPListEventProperties.List.Web
  • SPItemEventProperties.ListItem.Web
  • UnsecuredLayoutsPage.Web (and LayoutsPageBase.Web)
  • SPWebProvisioningProperties.Web
  • SPControl.GetContextWeb()
  • SPControl.GetContextSite()

In general, there are three common cases where you do own the object:

  • When you new up your own SPSite object:
    SPSite site = new SPSite(url);
  • When you explicitly call SPSite.OpenWeb():
    SPWeb web = site.OpenWeb(url);
  • When you iterate through SPSite.AllWebs – this is an extremely expensive operation, so avoid if possible:
    foreach(SPWeb web in site.AllWebs) {
    // …
    web.Dispose();
    }

If you think of any other examples I should include in these lists, leave a comment and I’ll update the post.

2. An SPWeb or SPSite object you own should be disposed as soon as it is no longer needed.

Stefan’s post has a great explanation, which he neatly summarizes:

You should dispose a SPWeb or SPSite object after the last access to a child object of this object. Be aware that a sub site (SPWeb) is not a child object. But (e.g.) a list or a folder or list item is a child object for this scenario.

3. Using a disposed SPWeb or SPSite object (like might happen if you Dispose something that doesn’t belong to you) can cause exceptions.

While it’s great to Dispose properly, it’s dangerous to Dispose improperly: when in doubt, in my opinion, err on the side of caution and let SharePoint clean up the mess. For webs, that happens when the parent site is disposed. For sites, that happens when the thread ends. If you do have memory problems, check out Stefan’s post on Troubleshooting SPSite/SPWeb leaks in WSS v3 and MOSS 2007.

4. SPSite and SPWeb objects should be disposed in the same method they get allocated.

And as a corollary to this, I agree with Chris O’Brien‘s conclusion, almost as an afterthought, in a recent post:

However, if the caller has to provide the IDisposable objects, it can then be responsible for calling Dispose() because it knows when they are no longer needed. Most often the caller will simply be passing a reference to SPContext.Current.Web, but if the caller happens to be a Feature receiver then SPFeatureProperties.ListItem.Parent.Web would be passed, or if no existing SPWeb object was available to the caller (e.g. console app) a new SPWeb object would have to be instantiated and passed. Disposals then become much simpler because they can always happen in the same place as any instantiations.

If the logic that utilizes the SPSite/SPWeb is encapsulated within a function that receives it as an argument, there is no risk of leaks. Most simply, there is little risk when the code looks like this:

using (SPWeb web = SPContext.Current.Site.OpenWeb()) {
  DoSomethingWith(web);
}

Now, some developers like to encapsulate the SPSite/SPWeb retrieval logic in dedicated methods. A safe approach is to leverage delegates. Unfortunately, variations of an unsafe alternative are much more common (in this case, copied from an otherwise brilliant MVP’s blog):

public static SPWeb ReturnSPWeb(string url) {
  using (var site = new SPSite(url)) {
    using (SPWeb web = site.OpenWeb()) {
      return web;
    }
  }
}

When you return from within a using block, the compiler will insert a Dispose on the used object before returning. Open your assembly in ildasm if you don’t believe me. So the above code is effectively equivalent to the following:

public static SPWeb ReturnSPWeb(string url) {
  var site = new SPSite(url);
  SPWeb web = site.OpenWeb();
  web.Dispose();
  site.Dispose();

  return web;
}

Oops! Before they even got a chance to do anything, the objects were cleaned up, and more importantly any code that depends on the returned SPWeb is completely unsafe! The same would apply for child objects (SPList, etc) returned from within a using block around the parent object. Even if the code works most of the time, the bugs it could introduce would be nearly impossible to track down.

Thanks again to Stefan for the excellent post!

Last updated 12/8/2008

12 Responses to “The New Definitive SPSite/SPWeb Disposal Article”

  1. Jeremy Thake Says:

    Be great to get this up on the new http://www.sharepointdevwiki.com/ Let me know if you’re happy to submit it.

  2. Safely Process SPSite.AllWebs « Solutionizing .NET Says:

    […] December 7, 2008 — Keith Dahlby Probably the ugliest scenario for properly disposing SPWeb objects is cleaning up when you enumerate SPSite.AllWebs. To simplify that task, I present a […]

  3. Links (12/7/2008) « Steve Pietrek - Everything SharePoint Says:

    […] **** The New Definitive SPSite/SPWeb Disposal Article […]

  4. Rick Says:

    Hi,

    I think you should add this one to the “Do not dispose” list as well
    SPWebProvisioningProperties.Web

    Cheers
    Rick

  5. Peter Seale Says:

    Keith, thanks for writing this article, it helped me out for sure.

  6. Keith Dahlby Says:

    @Rick ~ Agreed and done. I can’t find a definitive post on this online, but according to Reflector the Web property setter is internal and only set in SPWeb.DoProvisioningCallback to “this”, which should be cleaned up by its owner. Good catch!

    I also added SPControl.GetCurrentSite() and Web().

    @Peter ~ Clarity at last? We can only hope…

  7. Disposing list’s SPSite/SPWeb without ParentWeb « Solutionizing .NET Says:

    […] Disposing list’s SPSite/SPWeb without ParentWeb June 15, 2008 — Keith Dahlby Update 12/10/2008: Don’t use this technique; use the guidance here instead: The New Definitive SPSite/SPWeb Disposal Article […]

  8. Keith Dahlby Says:

    I’ll try to keep this post updated, but this article on the SharePoint Dev Wiki should have the community’s latest thoughts on disposal.

  9. Andy Says:

    What about a custom web service (.ASMX) placed in the Layouts folder, how does sharepoint know to automatically dispose of SPContext.Current.Web when the whole request is handled by code you wrote? (i.e does not extend one of the SharePoint classes) So the question is, where are these context objects disposed? I think they are not disposed.

    Also, is there a suggested pattern for itterating through large tree’s of SPWeb objects without having SPWeb * depthOfTree undisposed at the same time? So in otherwords itterate a tree of SPWeb Objects but only having one undisposed SPWeb at a time?

    • Keith Dahlby Says:

      Great questions, Andy. You’re fine to use SPContext.Current in a custom web service because ASMX still participate in the ASP.NET request lifecycle. SharePoint registers an IHttpModule (Microsoft.SharePoint.ApplicationRuntime.SPRequestModule) which takes responsibility for cleaning up the context objects.

      The most efficient way to iterate over a large tree of SPWeb objects is to use SPSite.AllWebs, which flattens the heirarchy and creates no parent-child web dependencies (unless you use ParentWeb or a member that requires it). If you have to navigate as a tree structure, you will need to leaving your chain of parent webs open. As you’re enumerating through a web’s child SPWebCollection, internally each successive web is created through the internal SPWeb+SPWebCollectionProvider class, whose OpenWeb() method depends on the parent web.

      Hope this helps ~
      Keith


Comments are closed.