views:

144

answers:

4

I've been using the following code in a .NET 1.1 SharePoint 2003 environment and it works great:

            try
            {
                site = SPControl.GetContextSite(Context);
                web = site.OpenWeb();
                ...
            }
            catch (Exception export)
            {
                output.Write("Caught Exception: <br/><br/>");
                output.Write(export.Message + "<br><br>");
                output.Write(export.StackTrace);
            }
            finally
            {
                if (web != null)
                    web.Dispose();
                if (site != null)
                    site.Dispose();
            }

However, I'm currently porting the code to a .NET 2.0 SharePoint 2007 environment and I get the following error message:

"Trying to use an SPWeb object that has been closed or disposed and is no longer valid."

If I comment out the Dispose() code, it works fine. But won't this cause memory leaks? What's the best way to fix the problem?

A: 

Put your code in using statements which will auto call dispose when the objects go out of scope.e.g.

using(site = SPControl.GetContextSite(Context))
{
    using(web = site.OpenWeb())
    {
    //your code
    }
}
Ben Robinson
see this document for the full explanation http://msdn.microsoft.com/en-us/library/aa973248(office.12).aspx
Joe Capka
The first using statement is *very bad*- it will explicitly dispose the SPSite object from GetContextSite which you shouldn't do as it will be used by other code - exactly what the OP's code is doing! Search "do not do this" in msdn link above
Ryan
+1  A: 

When handling disposable object, and SharePoint in particular, it is simpler to use using:

using(SPSite site = new SPSite("..."))
using(SPWeb web = site.OpenWeb("..."))
{

}

However, note that you should not dispose of elements that come from current context - they are shared, and disposing of them may cause this error. This is a common mistake, and in my opinion should have been handled better by the API.

See also - Using Disposable Windows SharePoint Services Objects:

SPContext objects are managed by the SharePoint framework and should not be explicitly disposed in your code. This is true also for the SPSite and SPWeb objects returned by SPContext.Site, SPContext.Current.Site, SPContext.Web, and SPContext.Current.Web.

Kobi
you might want to check the nesting of those using statements - as is won't compile as site is out of scope when you're using site.OpenWeb
Ryan
@​​​​​​​​​​​​​​Ryan - this compiles and works well: http://stackoverflow.com/questions/1329739/nested-using-statements-in-c The example isn't exactly the same, you'd claim, but I've done that many times.
Kobi
Me bad! You're right - sorry!
Ryan
+1  A: 

Also a better way to do the above:

site = SPControl.GetContextSite(Context);
web = site.OpenWeb()

is the following:

SPSite site = SPContext.Current.Site;
SPweb web = SPContext.Current.Web;

No memory links here. Seriously though; going to 2007 not 2010?

iambriansreed
+1  A: 

When you get the SPSite object from GetContextSite you are getting an object that is shared with everything else running on that page.

After you Dispose of it other code is trying to use it - hence the error.

You should only Dispose objects that you create yourself and not ones that you get with GetContextSite.

The quick fix is (as Iambriansreed says) is to remove the finally block and replace the first two lines with.

SPSite site = SPContext.Current.Site; // Do you even need this?
SPweb web = SPContext.Current.Web;

Better performance and no memory leak as the SharePoint framework handles both creating and disposing of objects retrieved through SPContext.

However - its a complex and poorly understood subject so a thorough read through of the Best Practices: Using Disposable Windows SharePoint Services Objects and using the SPDisposeCheck tool is essential.

Ryan