Dedicated to development, configuration, and tips and tricks for both WSS 3.0 and MOSS

Posted by Aaron Varga on Saturday, 31 Jan 2009 01:38

I’ve been waiting for this for a couple months after an announcement was made that it was going to be released, and finally today the SPDisposeCheck tool has been released!  The tool reviews a .NET Assembly (DLL or EXE) and evaluates SharePoint API's used in that assembly. It will produce a report identifying where code doesn't follow best practices for memory management in SharePoint.

To test this out, I created a simple console application that contains undisposed SharePoint objects:

static void Main(string[] args) {
    SPSite siteCollection = new SPSite("http://server");
    SPWeb site = siteCollection.OpenWeb();

    // do something
}


As you know, you must always be sure to dispose of your SPSite and SPWeb objects, as this can lead to expensive memory leaks.  If you’re not a seasoned SharePoint developer, this may not be obvious to you, and for this the SPDisposeCheck tool is a huge asset.  I ran the following command after downloading the tool:

SPDisposeCheck.exe C:\Projects\Test\Aaron.TestApp\bin\Aaron.TestApp.exe

Which produced the following:

image


You can see that 2 errors were found.  Specifically, this tool points out the following for the first error:

Disposable type not disposed: Microsoft.SharePoint.SPSite
***This may be a false positive depending on how the type was created or if it is disposed outside the current scope


This tells me that my SPSite object needs disposed. The second error is exactly the same thing, except in reference to SPWeb.  Let’s clean up the code, this time disposing of our objects:

using (SPSite siteCollection = new SPSite("http://server")) {
    using (SPWeb site = siteCollection.OpenWeb()) {
        // do something
    }
}


Now, when I run this tool I get the following:

image

 

There’s a pretty good chance that if you’ve even made it this far in this post, that you’ve written SharePoint code before, and already know to always dispose of your SPSite/SPWeb objects.  However, what about the less obvious examples.  I ran this tool on an assembly I’m currently developing for a client, and I got an error:

Notes: Dispose/Close was not called on SPLimitedWebPartManager.Web


I went back through my code, and found this line:

SPLimitedWebPartManager manager = defaultPage.GetLimitedWebPartManager(PersonalizationScope.Shared);

Basically this gets the web part manager for a specific page, so you can add/delete/modify the web parts that are on it.  Why is this throwing an error?  I’m not using an SPSite OR an SPWeb anywhere!  It’s because there’s a very non-obvious memory leak in this that creates its own SPWeb object, and this object needs to be disposed.  The correct usage of this is:

using (SPLimitedWebPartManager manager = 
defaultPage.GetLimitedWebPartManager(PersonalizationScope.Shared)) { // do something if (manager.Web != null) manager.Web.Dispose(); }


Not at all obvious, but I’m glad this tool caught it!  PLEASE, if you are writing Object Model code, run this tool.  For a ton more information on SharePoint Dispose() patterns, Roger Lamb’s post is by far the best one out there.  Also check out this MSDN article.

You can (and really should) download this tool here.

Posted by Aaron Varga on Friday, 9 Jan 2009 09:15

Although this was published over a year ago, I just happened to stumble on this today.  It’s definitely a good read for SharePoint developers, specifically those of us who use the Object Model to access list data.  The Working With Large Lists in Office SharePoint Server 2007 whitepaper available from Microsoft covers in gruesome details the following 8 different ways to retrieve list data, and even includes performance metrics for each different option:

  • Through the browser
  • SPList with For/Each
  • SPList with SPQuery
  • SPList with DataTable
  • SPListItems with DataTable
  • Lists Web Service
  • Search
  • PortalSiteMapProvider

One thing I did want to explicitly point out is the use of the PortalSiteMapProvider to retrieve list data.  Before reading this, I didn’t even know this was an option, but could be a very efficient option for retrieving list data in certain situations. Read the suggestions below to determine if this is the best option for your scenario.  The PortalSiteMapProvider was originally created to help cache content for navigation. However, it also provides a nice automatic caching infrastructure for retrieving list data. The class includes a method called GetCachedListItemsByQuery which first retrieves data from a list based on an SPQuery object that is provided as a parameter to the method call. The method then looks in its cache to see if the items already exist. If they do, the method returns the cached results, and if not, it queries the list, stores the results in cache and returns them from the method call. Here’s a quick example:

public void GetListItemsFromSiteMapProvider() {
    using (SPSite siteCollection = new SPSite("http://server")) {
        using (SPWeb site = siteCollection.RootWeb) {
            string listName = "ListName";
            string camlQuery = "<Where><Eq><FieldRef Name='Title'/><Value Type='Text'>Value</Value></Eq></Where>";

            SPList list = site.Lists[listName];
            SPQuery query = new SPQuery();
            query.Query = camlQuery;

            PortalSiteMapProvider provider = PortalSiteMapProvider.CurrentNavSiteMapProviderNoEncode;
            PortalWebSiteMapNode node = provider.FindSiteMapNode("/") as PortalWebSiteMapNode;                   
            SiteMapNodeCollection results = provider.GetCachedListItemsByQuery(node, listName, query, site);

            foreach (PortalListItemSiteMapNode item in results) {
                // do something for each item
            }

            site.Close();
        }
        siteCollection.Close();
    }
}

Considerations
Using PortalSiteMapProvider in this way can have some drawbacks. The first request for a particular web or page via FindSiteMapNode or GetChildNodes will take longer to fetch than a similar call using the traditional Object Model calls. This is because in this case the caching mechanism must perform a round trip to the database and will often fetch more data than it needs in an attempt to pre-fetch and optimize subsequent requests. Therefore it is not useful to fetch data which is accessed very infrequently via these methods. The benefit associated does not show up until multiple calls have been made for the same data.

In addition, using the above methods to fetch data which changes frequently can also be counter-productive. If the data changes frequently, it will be frequently invalidated and re-fetched from that database negating the benefits of caching. The invalidation mechanism is relatively coarse, with a change to a particular item invalidating any data in the same web as the item.

I bet you didn’t realize there were so many ways just to get list data, huh?  Before beginning a project where you will need to access data from a SharePoint list, read the whitepaper and please consider the different data access methods available to you and make sure you choose the right one, as certain methods are much better than others in various scenarios.