Microsoft released SPDisposeCheck v1.3.1 back in November 2008 and the Best Practices: Using Disposable Windows SharePoint Services Objects article (updated Jan 2009) to help WSS and MOSS developers to create better SharePoint custom code using the SharePoint object model. The tool and article specifically target the potential danger of memory leaks that can arise from using the SharePoint API. They are a "must have / must read" for every SharePoint developer.

We have been using the SPDisposeCheck in a number of projects already. Our development team uses Team Foundation Server to create builds for our SharePoint deployment packages. As part of our continuous integration effort, we have a custom build target that runs SPDisposeCheck on the build server against the output DLLs once the compilation is complete. This ensures that we use the SPDisposeCheck tool each and every time we create a build so that we can identify memory leak issues as quickily as a build is created. If some checked in code has a memory leak identified by SPDisposeCheck, the build will fail and we will have to fix the memory leak before we continue with the build. Recently I ran into an issue that is not well described in the best practice article. The issue has something to do with the SPLimitedWebPartManager and the implicit SPWeb object that the SPLimitedWebPartManager references.

In the article, it states: "Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager The SPLimitedWebPartManager class contains a reference to an internal SPWeb object that must be disposed." But exactly how the SPWeb object is obtained by the SPLimitedWebPartManager is not detailed in the article. The sample code states the proper coding practice using the SPLimitedWebPartManager is to dispose the internal SPWeb object explicitely before the block of code involving the SPLimitedWebPartManager exits, and SPDisposeCheck would give you a SPDisposeCheckID_160 flag if you fail to implement this coding practice:

// The following code is copied from http://msdn.microsoft.com/en-us/library/aa973248.aspx
void SPLimitedWebPartManagerLeak()
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
            SPLimitedWebPartManager webPartManager =
                page.GetLimitedWebPartManager(PersonalizationScope.Shared);
                webPartManager.Web.Dispose();
        } // SPWeb object web.Dispose() automatically called.
    }  // SPSite object siteCollection.Dispose() automatically called.
}

This is great if you are using the SPLimitedWebPartManager in your current class only once. What I ran into is quite interesting. My situation: I want one feature to help me change two Web Parts on two different pages in the same web. Initially, I created two methods, each modifies one web part on one of the pages. Each method implements the above coding pattern and disposes the internal SPWeb object of the SPLimitedWebPartManager explicitly. My Feature Receiver would then invoke Method1 and then Method2 to modify the Web Parts in sequence on activation. That piece of code compiled properly, and it would also pass the SPDisposeCheck test. But it fails at runtime and throws an SPException complaining the SPWeb object being referenced is no longer available. ([Microsoft.SharePoint.SPException] = {"Trying to use an SPWeb object that has been closed or disposed and is no longer valid."}) Surprise! The code looks something like:

//The following code sample would pass SPDisposeCheck but would not run properly
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            Method1(web);
            Method2(web);
        }
    }
}

private void Method1(SPWeb web)
{
    SPFile page1 = web.GetFile("some url for page1");
    SPLimitedWebPartManager mgr = page1.GetLimitedWebPartManager(PersonalizationScope.Shared);

    // do something with the web parts obtained from the mgr

    mgr.Web.Dispose();  //dispose by Method1
}

private void Method2(SPWeb web)
{
    SPFile page2 = web.GetFile("some url for page2");
    SPLimitedWebPartManager mgr = page2.GetLimitedWebPartManager(PersonalizationScope.Shared);

    // do something with the web parts obtained from the mgr

    mgr.Web.Dispose();  // dispose by Method2
}

Further investigation revealed that the internal SPWeb object of the SPLimitedWebPartManager in the two methods are referencing the same SPWeb object. When Method1 calls mgr.Web.Dispose(), the SPWeb instance of the SPLimitedWebPartManager is disposed. When Method2 obtains the SPLimitedWebPartManager using the page2.GetLimitedWebPartManager() method within the context of the same feature receiver, the SPLimitedWebPartManager with a reference to the SPWeb object already disposed is passed to Method2. And when Method2 tries to dispose the SPWeb object, it is no longer available. As a matter of fact, the API will generate the SPException as soon as you reference SPLimitedWebPartManager.Web object before a Dispose() can be called. So the common practice of checking if SPLimitedWebPartManager.Web is null before calling Dispose() will not work.

The workaround for this small inconvenience is to change the code construct so that you only need to dispose the SPLimitedWebPartManager.Web object once in your class. One easy fix is to change the two methods so that we don't have to manage two instances of SPLimitedWebPartManager and the internal SPWeb objects. The sample code construct below would work better:

//The following code sample would pass SPDisposeCheck and run as expected
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page1 = web.GetFile("Source_Folder_Name/Source_Page1");
            SPLimitedWebPartManager webPartManager =
                page1.GetLimitedWebPartManager(PersonalizationScope.Shared);

            Method1(webPartManager);

            SPFile page2 = web.GetFile("Source_Folder_Name/Source_Page2");
            webPartManager = page2.GetLimitedWebPartManager(PersonalizationScope.Shared);

            Method2(webPartManager);

            //dispose the SPWeb object
            webPartManager.Web.Dispose();  // dispose is called only once
        }
    }
}

private void Method1(SPLimitedWebPartManager mgr)
{
    // do something with the web parts obtained from the mgr, but do not call mgr.Web.Dispose()

}

private void Method2(SPLimitedWebPartManager mgr)
{
    // do something with the web parts obtained from the mgr, but do not call mgr.Web.Dispose()

}

Hope this helps you to get around any strange coding issues regarding disposing SPWeb when using SPLimitedWebPartManager. As an additional bonus question, do you know why the following webPartManager.Web.Dispose() would fail with the same SPException?

//What is wrong with this?
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    using (SPSite siteCollection = new SPSite("http://moss"))
    {
        SPLimitedWebPartManager webPartManager = null;

        using (SPWeb web = siteCollection.OpenWeb())
        {
            SPFile page1 = web.GetFile("Source_Folder_Name/Source_Page1");
            webPartManager = page1.GetLimitedWebPartManager(PersonalizationScope.Shared);

            Method1(webPartManager);      

            SPFile page2 = web.GetFile("Source_Folder_Name/Source_Page2");
            webPartManager = page2.GetLimitedWebPartManager(PersonalizationScope.Shared);

            Method2(webPartManager);            

        }

        webPartManager.Web.Dispose();

    }
}

Share