we all know that disposing SPSite/SPWeb objects is the best practice that will avoid any memory leaks. If you know the first point then you should also know when to dipose?
Objects accessed from the SPContext or properties(feature) do not need to be explicitly disposed but where as instantiated as below need to be.
SPSite site = new SPSite("http://abc.com");
So just wrap the above line of code with using that will automatically dispose the SPSite object when its out of scope.
Using(SPSite site = new SPSite("http://abc.com"))
{
.....
}
There are some very special cases where we get confused, which I gonna cover here,
SPWeb web = site.RootWeb/ParentWeb
or
SPWeb web = SPcontext.Current.Site.RootWeb/ParentWeb
Should the web object be disposed in the above scenario?Certainly NOT, as these objects are disposed implicitly when site object is disposed i.e wrapped "using" when it is instantiated and in other case by sharepoint when it(spsite) is from SPContext.
That being said how about SPWeb from SPContext.Current.Site.OpenWeb() ?
From the reflector it can be seen that a new instance of SPWeb is created when OpenWeb is called, irrespective of the way SPSite is instantiated.So in the code SPWeb should be disposed always explicitly.
Also, SPLimitedWebPartManager implements IDisposable which means it should be disposed when ever used.
we always follow the below practice when it comes to SPLimitedWebPartManager
using (SPWeb web = site.OpenWeb())
{
using (SPLimitedWebPartManager webPartManager = web.GetLimitedWebPartManager(url, scope))
{
webPartManager.AddWebPart(mstrReport, entry.Key.ToString(), 0);
webPartManager.SaveChanges(mstrReport);
}
}
I had a doubt if SPlimiedWebPartManager should be disposed when it is read from SPContext as below,
SPContext.Current.Web.GetLimitedWebPartManger(..)
So quickly i opened the reflector to see what is happening in the background, then i realized that it should be disposed and along with it the property, web.
Here is the snippet from reflector.
internal SPLimitedWebPartManager GetLimitedWebPartManagerInternal(Uri pageUrl, int pageVersion, PageView requestedView, bool forRender, bool includeHidden)
{
if (null == pageUrl)
{
throw new ArgumentNullException("pageUrl");
}
if (!pageUrl.IsAbsoluteUri)
{
throw new ArgumentException(WebPartPageResource.GetString("AbsoluteUriRequired"), "pageUrl");
}
this.EnsureAspx(this.GetWebRelativeUrlFromUrl(pageUrl.ToString(), false, true));
SPWeb web = this.Site.OpenWeb(); // THIS WEB IS NEVER DISPOSED HERE
web.IsOwnedByWebPartManager = true;
SPWebPartManager manager = null;
try
{
long num;
if (this.AllowUnsafeUpdates)
{
web.AllowUnsafeUpdates = this.AllowUnsafeUpdates;
}
manager = web.GetWebPartManagerInternal(pageUrl, pageVersion, requestedView, forRender, includeHidden, out num);
}
catch
{
web.Dispose();
throw;
}
if (manager != null)
{
return manager.LimitedWebPartManager;
}
return null;
}
The above web instance created by sharepoint in the method is not disposed. So i checked if it is doing it in SPLimitedWebPartManager.Dipose()
public void Dispose()
{
if (!this.m_disposed)
{
if (this.m_manager != null)
{
this.m_manager.Dispose();
}
this.m_webParts = null;
this.m_webPartDefs = null;
this.m_manager = null;
this.m_disposed = true;
}
}
Ah, Its not doing even here which clearly shows that it should be explicitly disposed by the developers.
So best practice gonna be as below.
using (SPWeb web = site.OpenWeb()) // SPWeb web = SPContext.Current.Web
{
using (SPLimitedWebPartManager webPartManager = web.GetLimitedWebPartManager(url, scope))
{
try
{
webPartManager.AddWebPart(mstrReport, entry.Key.ToString(), 0);
webPartManager.SaveChanges(mstrReport);
}
finally
{
// this web is created by sharepoint but not the above one
webPartManager.Web.Dispose().
}
}
}