Sunday, October 19, 2008

Properly disposing SPWeb.Webs items

A while ago I read a great article by Roger Lamb that shows how to properly use and dispose SPSite and SPWeb instances: SharePoint 2007 and WSS 3.0 Dispose Patterns by Example.

One of dispose patterns that he showed is about SPWeb.Webs items disposal. He first showed one of the wrong ways you can use SPWeb.Webs collection:


void WebsLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
// SPWeb innerWeb leak
}
} // SPWeb object outerWeb.Dispose() automatically called
} // SPSite object siteCollection.Dispose() automatically called
}

Then he showed the right way to handle SPWeb.Webs collection:


void WebsBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
innerWeb.Dispose();
}
} // SPWeb object outerWeb.Dispose() automatically called
} // SPSite object siteCollection.Dispose() automatically called
}

You can see on line 9 that "innerWeb" is disposed as it should be. The thing is that Roger didn't show that innerWeb instance must be used in either "using" block or in "try" block with disposal in "finally" block. If "innerWeb" is not used in "using" block (or in "try & finally") then it may happen that an exception is thrown before innerWeb.Dispose method is called.

I didn't think it is significant and assumed developers will recognize this as a potential leak and will always use "innerWeb" in "using" block. After some time I saw this mistake in source code of web parts being used in production environment and I instructed developers who wrote the code to fix the potential issue. Then I saw the same bug a few days ago on a lecture called "MOSS 2007 for developers - how to avoid mistakes" on a Microsoft conference I attended in Novi Sad.

I thought I should blog about this and point other developers to this issue. Next code shows one of the proper ways SPWeb.Webs should be used and disposed:


void WebsBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
SPWebCollection innerWebs = outerWeb.Webs;
if (innerWebs != null && innerWebs.Count > 0)
{
foreach (SPWeb innerWeb in innerWebs)
{
using (innerWeb)
{
//use innerWeb here
}
}
}
} // SPWeb object outerWeb.Dispose() automatically called
} // SPSite object siteCollection.Dispose() automatically called
}

You can see in line 12 that "innerWeb" is used in "using" block. No matter if an exception happens in line 14, the "innerWeb" will be disposed.

No comments: