Sunday, December 21, 2008

Best Practices disposing Sharepoint objects - Reference Guide

Frequently developers use the Sharepoint object model either to read or write data into Share point database via Lists/Libraries. Here comes the need to dispose Sharepoint objects, even though they are created as managed objects, these objects use unmanaged code and memory to perform a majority of their work.

Because the smaller managed part of the object does not put memory pressure on the garbage collector, the garbage collector does not release the object from memory in a timely manner. The object's use of a large amount of unmanaged memory can cause some of the unusual behaviors described as follows:

  • Frequent recycles of the Microsoft Windows SharePoint Services application pool, especially during peak usage
  • Application crashes that appear as heap corruption in the debugger
  • High memory use for Microsoft Internet Information Services (IIS) worker processes
  • Poor system and application performance
Applications that work with IDisposable objects in MOSS must dispose of the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically.

Unusual behaviors mentioned above occur when Sharepoint objects are not disposed off properly or explicitly by the devlopers. Here is a reference guide on Sharepoint Coding best practices with relevant scenarios and examples of each type:

Three techniques to ensure that your objects get disposed properly:

  • Dispose method
  • using clause
  • try, catch, and finally blocks
Whenever you wish to free memory resources, call Dispose method, instead of the Close method , internally Dispose method calls Close method itself, the reason being that standard .NET Framework process calls the Dispose method to free memory resources associated with object.

Thankfully Microsoft has recently launched Sharepoint Dispose Checker utility. It will ensure that you have properly disposed objects so that you aren't consuming more memory. All developers should run this utility after they use Sharepoint object model. Click here to download SPDispose Utility.

NOTE : This post is applicable for both SharePoint 2010 and SharePoint 2007

Best Practice #1: Use the Using Clause for all Sharepoint objects that implement the IDisposable interface
C# Coding snippet

String str;

using(SPSite oSPsite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}

Best Practice #2: Use the try/catch/finally blocks. When you use try blocks, it is important to add a finally block to ensure that all objects are disposed off properly:

C# Coding snippet

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
oSPSite = new SPSite("http://server");
oSPWeb = oSPSite.OpenWeb(..);

str = oSPWeb.Title;
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();
}

Best Practice #3: Using Response.Redirect in the try block will never execute the finally block, so it is important, to dispose off all objects before the redirection occurs:

C# Coding snippet
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
bool bDoRedirection = true;

try
{
oSPSite = new SPSite("http://server");
oSPWeb = oSPSite.OpenWeb(..);

str = oSPWeb.Title;
if(bDoRedirection)
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();

Response.Redirect("newpage.aspx");
}
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();
}

Best Practice #4: Whenever you create an object with a new operation, the creating application must dispose it off

C# Coding snippet

SPSite oSPSite = new SPSite("http://server");

... additional processing on SPSite ...

oSPSite.Dispose();

alternatively, this is a better approach

C# Coding snippet

using(SPSite oSPSite = new SPSite("http://server"))
{
... additional processing on SPSite ...
}

Best Practice #5: For Site.OpenWeb method, you need to dispose it off explicitly.

C# Coding snippet

String str;
SPSite oSPSite = new SPSite("http://server");
SPWeb oSPWeb = oSPSite.OpenWeb();

str = oSPWeb.Title;
str = oSPWeb.Url;

... additional processing on SPWeb ...

oSPWeb.Dispose();
oSPSite.Dispose();

Alternatively you may use the Using clause too for better readibility and automatic disposition of objects:

C# Coding snippet

String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;

... additional processing on SPWeb ...
}
}

Best Practice #6: An exception to the rule is that One should not explicitly dispose SPSite.RootWeb, as it is automatically disposed off.

Similary one should not explicity dispose SPContext.Current.Site and SPContext.Current.Web as they are handles automatically by Sharepoint and .NET framework.

C# Bad Coding Practice Snippet:

void SPContextBADPractice()
{
SPSite siteCollection = SPContext.Current.Site;
siteCollection.Dispose(); // DO NOT DO THIS
SPWeb web = SPContext.Current.Web;
web.Dispose(); // DO NOT DO THIS
}


C# Good Coding Practice Snippet:

void SPContextBestPractice()
{
SPSite siteCollection = SPContext.Current.Site;
SPWeb web = SPContext.Current.Web;
// Do NOT call Dispose()
}
Best Practice #7: SPControl.GetContextSite(Context) and GetContextWeb(Context) return SPSite and SPWeb respectively, they do not need an explicit call to Dispose(), they will be disposed automatically

C# Bad Coding Practice Snippet:

void SPControlBADPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
siteCollection.Dispose();   // DO NOT DO THIS
SPWeb web = SPControl.GetContextWeb(Context);
web.Dispose();  // DO NOT DO THIS
}

C# Good Coding Practice Snippet:

void SPControlBestPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
SPWeb web = SPControl.GetContextWeb(Context);
// Do NOT call Dispose()
}

Best Practice #8: SPWeb.ParentWeb  returns SPWeb, and needs to be disposed off explicitly using either Dispose() or the using clause

C# Bad Coding Practice Snippet:
void ParentWebLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
SPWeb parentWeb = outerWeb.ParentWeb; // Internal reference to SPWeb parentWeb
string sTitle = parentWeb.Title;
string sUrl = parentWeb.Url;
// SPWeb object parentWeb leaked
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:
void ParentWebBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
using (SPWeb parentWeb = outerWeb.ParentWeb) // Internal reference to SPWeb parentWeb
{
string sTitle = parentWeb.Title;
string sUrl = parentWeb.Url;
} // SPWeb object parentWeb.Dispose() automatically called
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #9: SPWeb.Webs returns an SPWeb object, practically used in drilling down sub-sites within a Site

C# Bad Coding Practice Snippet:
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 
}


C# Good Coding Practice Snippet:
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 
}

Best Practice #10: AllWebs[] Indexer returns SPWeb
object that needs to be disposed to avoid aggregation of memory which
can lead to memory pressure when running on a site collection with
large number of sub sites. Practically used in Enumerating or iterating all webs in a site collection.

C# Bad Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
 // Explicit Dispose must be called to avoid aggregation of memory
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}


C# Good Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
innerWeb.Dispose();   // Explicit Dispose must be called to avoid aggregation of memory
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Bad Coding Practice Snippet:

void AllWebsIndexerLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb web = siteCollection.AllWebs[0];
// SPWeb web leaked
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:

void AllWebsIndexerBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs[0])
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #11: AllWebs.Add() returns a instance of SPWeb object which needs to be disposed.

C# Bad Coding Practice Snippet:
void AllWebsAddLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
// SPWeb web Leaked
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:
void AllWebsAddBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs.Add("site-relative URL"))
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #12: OpenWeb() returns a SPWeb object which needs to be disposed.

C# Bad Coding Practice Snippet:
void OpenWebLeak()
{
using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
{
// SPSite leaked !
} // SPWeb object web.Dispose() automatically called
}

C# Good Coding Practice Snippet:
void OpenWebBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called
}

Best Practice #13:new SPSite() - Instantiating SPSite objects with the new operator needs to be disposed.

C# Bad Coding Practice Snippet:
void CreatingSPSiteLeak()
{
SPSite siteCollection = new SPSite("http://moss");
// siteCollection leaked
}

C# Good Coding Practice Snippet:
void CreatingSPSiteExplicitDisposeBestPractice()
{
SPSite siteCollection = new SPSite("http://moss");
siteCollection.Dispose();
}

void CreatingSPSiteWithAutomaticDisposeBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
} // SPSite object siteCollection.Dispose() automatically called
}

Do Not Dispose Guidance for SharePoint 2010 & 2007:(Updated on 23/2/2011):
Do not dispose the following listed objects explicitly:

•SPContext.Current.Site

•SPContext.Current.Web

•SPContext.Site

•SPContext.Web

•SPControl.GetContextWeb(..)

•SPControl.GetContextSite(..)

•SPFeatureReceiverProperties.Feature.Parent

•SPItemEventProperties.ListItem.Web

•SPList.BreakRoleInheritance()

◦Do not call list.ParentWeb.Dispose()

•SPListEventProperties.Web

•SPListEventProperties.List.Web

•SPSite.RootWeb

◦Problems may occur when SPContext.Web has equality to the SPContext.Web.. make sure you dispose of SPSite and it will cleanup sub webs automatically

•SPSite.LockIssue

•SPSite.Owner

•SPSite.SecondaryContact

•SPWeb.ParentWeb

•SPWebEventProperties.Web

Changes:

•Microsoft.SharePoint.WebControls.SiteAdminsitrationSelector.CurrentItem

◦When used with WSS 3.0 you must call Dispose(), with SharePoint Foundation 2010 you don’t.

•Event Receivers and properties.OpenWeb()

◦WSS 3.0: When you call properties.OpenWeb() the returned SPWeb will need to call Dispose()

◦SharePoint Foundation 2010: Use the newly introduced SPItemEventProperties.Web property instead of SPItemEventProperties.OpenWeb() for better performance and to avoid the need to call Dispose().


Sources: Compiled from MSDN and Roger Lamb's blog site If you find the Coding practices helpfull, please drop in your valuable comments...

3 comments:

  1. Excellent! Thanks for this - I've been looking at this feature for ages. Followed your instructions and it works a treat!

    ReplyDelete