Thursday, March 12, 2009

SPException: Trying to use a SPWeb object that has been closed or disposed and is no longer valid

Ever seen this error? See if it sounds familiar…

Just about any developer working long enough with SharePoint will, at some point, wake up in the middle of the night with the words "Memory Leak" echoing in through their head... No - it is not the cat's fault, and no it does not mean you forgot to take out the trash again... What did happen? You came across one of Microsoft's "best practices" articles regarding disposing of SPWeb and SPSite objects in SharePoint…

According to the original MS guidance, every SPWeb or SPSite object you create (or get) that was not initiated within the page's context (i.e. not SPContext.Current.Web and not SPControl.GetContextWeb(Context) ) must be disposed of.

It used to be that the "using" statement construct was recommended to avoid forgetting to dispose… in other cases you'd put up a flag and dispose of it at the end of your code's life cycle - either way was good enough.

Being the "good guys" that we are, we went over and updated all of our codes and components to match these guide lines.
And what happened? Every once in a while disposing these objects would cause baffling errors such as: "Trying to use a SPWeb object that has been closed or disposed and is no longer valid".

But hey, if we call SPContext.Current.Site.RootWeb - the best practices guidelines said we have to dispose of it! Same goes for SPContext.Current.Web.Site and SPContext.Current.Web.Site.RootWeb etc.

Recently I came across a great article by Roger Lamb showing a few updates from Microsoft regarding these disposable objects and recommendations.

So - pay close attention to these updates… it just might help you sleep through the night!

* When making a call to SPContext.Current.Web.Site.RootWeb - you now have to dispose only of the SPSite object: SPContext.Current.Web.Site!

Basically, you should NOT dispose of the SPSite.RootWeb object directly.

* When calling SPWeb.ParentWeb - you should NOT dispose of the ParentWeb object!

* Same goes to SPList.ParentWeb - do NOT dispose of the SPWeb object.

Other updates I found interesting (and frustrating) are:
* Calling SPWeb.AllWebs collection requires you to dispose of this collection object.
* Creating a new site by SPWeb.Webs.Add() - need to dispose of the web object returned.
* Calling GetLimitedWebPartManager() method? It will create a SPWeb object of its own and not dispose of it properly.

Please review the complete updated best practices guide at MSDN here:
http://msdn.microsoft.com/en-us/library/aa973248.aspx

I hope Microsoft will release an update for this issue soon.. Just thinking of all the code lines I wrote under the wrong best practices article is making me dizzy...

Good luck and happy disposing!

Shai Petel.

=~=~= Update =~=~=
Ok, this explains why it took me so long to find this problem...
I had this code in my web part, written according to the original best practices:
using(SPSite site = SPContext.Current.Web.Site)
{
    using(SPWeb web = site.RootWeb)
    {
        //Some code here...
    }
}

I cannot explain this but I only got this error on some sites, and on others it worked OK. Strange huh?
What I found is that this code will throw exception on any team site I created within a site collection that is not in the web application root path (i.e. sites under http://server/sites/*).

Anyway - according to the new guidelines, I should not dispose of the RootWeb object in this case. If I dispose of the SPSite object - it disposes of the RootWeb object -in this case my context web object...
So I updated my code to something like this and it worked:
SPWeb web = SPContext.Current.Site.RootWeb;//No disposing
//Some code here...

2 comments:

Olaf Didszun said...

Hello Shai,

I've exactly this behaviour with KWizCom WikiPlus, when the wikipage contains a webpart and I insert a wiki-tag ([[foo]]). After saving the element, I see "Trying to use...".
Any ideas how to solve this?

Olaf

Shai Petel said...

Hi Olaf,

This means your control or another control on the page is disposing of a context object by mistake.

What you can do is:
1. Get latest version of WikiPlus
2. Test your component on a seperate web part page, only place it several times on the same page.

If it is working on your seperate page and not inside wiki plus our support can help.

Thanks.