Not quite! Every SPSite tracks the SPWebs that it has emitted. A internal list named “m_openedWebs” holds these references. It doesn’t matter from which member of the SPSite you will get the SPWeb (RootWeb, OpenWeb, AllWebs, Add) the SPSite holds a reference on it. When you dispose the SPSite it loops through all SPWebs in “m_openedWebs” and calls the Close method on each. The dispose method of SPWeb does nothing else than calling it’s close method. Means when you dispose a SPSite, all SPWebs that it has emitted will be disposed implicitly! A construct like this
using(SPSite site = new SPSite("http://server/site"))
{
using(SPWeb web = site.OpenWeb())
{
//...
}
}is not necessary, because the using for the SPSite will implicitly dispose all of it SPWebs. By the way if you call the dispose method of the SPWeb explicitly it will internally remove itself from the “m_openedWebs” list. Sure there are scenarios where explicitly disposing of SPWebs is important, for example when you loop through the AllWebs property of the SPSite with maybe hundreds of SPWebs. Because every SPWeb consumes a lot of memory! On the other hand you don’t have to be afraid when you pass a SPWeb reference across your API. When you finally dispose the SPSite, all SPWebs it has emitted will be disposed too and you will not cause a memory leak. I think the SharePoint team have implemented this pretty well. What could cause problems indeed is when you still use a SPWeb that has already been disposed, doesn’t matter if it has been disposed implicitly or explicitly. The SPWeb will be reopened but will not be listed in the “m_openedWebs” of the SPSite. Means it can not be implicitly disposed by the SPSite anymore. A better and approach would have been to throw an ObjectDisposedException on all members after the SPWeb has been disposed.When you create an instance of SPSite you are responsible to dispose it. When to dispose SPWeb that have been emitted by your SPSite depends on the scenario.
You don’t believe? Just take a look at the following snippet or even better let it run:
Console.WriteLine("Create a SPSite");
SPSite site = new SPSite("http://localhost:101");
Console.WriteLine("Open 5 SPWebs...");
SPWeb web1 = site.RootWeb;
SPWeb web2 = site.OpenWeb();
SPWeb web3 = site.OpenWeb("my");
SPWeb web4 = site.AllWebs[1];
SPWeb web5 = site.AllWebs.Add(Guid.NewGuid().ToString());
int openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;
Console.WriteLine("Acutally opened SPWebs: " + openedWebs.ToString());
Console.WriteLine("Dispose web1 explicitly");
web1.Dispose();
openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;
Console.WriteLine("Dispose the SPSite");
site.Dispose();
openedWebs = (typeof(SPSite).InvokeMember("m_openedWebs",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, site, null) as ICollection).Count;
Console.WriteLine("Acutally opened SPWebs: " + openedWebs.ToString());
Console.ReadKey(); Any feedback?
6 comments:
Schöne Erklärung.
Der Dispose Checker ist aber anderer Meinung. Ok das ist er ja meistens :)
SharePoint.BestPractices : In method ... Dispose was not called on fileWeb
I suppose the Dispose Checker is wrong! Check out the SPSite with .NET Reflector.
I know. The Dispose Checker isn't always right. Perhaps we should tell them to change their check routines.
If you have about 100 sub sites, you will see why disposing inside the for loop is necessary. The best practice is that we should keep them in the memory as short as possible.
Hello, that's right for this scenario!
wieso sollte man eine 100 prozent lösung (dispose aufrufen) nicht einfach verwenden??
sehe da keinen nachteil und im gegenzug keinen vorteil dispose nicht aufzurufen...
Post a Comment