有的时候在SharePoint的log中会发现这样的log信息:
Detected use of SPRequest for previously closed SPWeb object.
Please close SPWeb objects when you are done with all objects obtained from them, but not before.
这个错误信息与SPSite、SPWeb这种非托管的对象没有正确释放有关系,或者释放早了,或者释放晚了,或者把不应该释放的给释放了。
以下是正确释放SPSite、SPWeb非托管对象的一些规则(摘录并翻译自《SharePoint 2010 as a Development Platform》):
1.当你释放一个SPSite对象的时候,通过这个SPSite获取的所有SPWeb对象都将被释放。
2.用SPSite/SPWeb对象的时候就创建,不用的时候就释放,宁可多次创建和释放也不要一直不释放。
3.使用using来确保SPSite/SPWeb对象的释放,以下是一个正确创建和释放的例子:
void GetList(string webUrl, string listName)
{
using(SPSite site = new SPSite(webUrl))
{
using(SPWeb web = site.openWeb())
{
try
{
GetListHelper(web, listName);
}
catch{}
}
}
}
void GetListHelper(SPWeb web, string listName)
{
SPList list = web.Lists[listName];
...
}
4.只释放你自己创建的SPSite和SPWeb对象。
5.通过GetLimitedWebPartManager获得的SPLimitedWebPartManager对象会引用一个SPWeb对象,这种情况下,需要显式的释放掉它:
SPFile page = web.GetFile("default.aspx");
using (SPLimitedWebPartManager webPartManager = page.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
try
{
// Do stuff
}
finally
{
webPartManager.Web.Dispose();
}
}
6.不要释放下列的属于SharePoint的SPSite和SPWeb对象:
SPSite site = SPContext.Current.Site;
SPWeb rootWeb = site.RootWeb;
SPWeb web = SPContext.Web;
SPWeb web = SPContext.Current.Web;
SPSite site = SPContext.Site;
SPSite site = SPContext.Current.Site;
7.在遍历子站点集合的时候需要显式释放SPWeb对象例如:
foreach (SPWeb subweb in rootweb.Webs)
{
// Do stuff
}
这段代码遍历了子站点集合,在执行的过程中,SharePoint会隐式调用OpenWeb方法,因此正确的方法是显式的释放SPWeb对象:
for (int i = 0; i <= rootweb.Webs.Count; i++)
{
using (SPWeb subweb = rootweb.Webs[i])
{
// Do stuff
}
}
或者:
foreach (SPWeb subweb in rootweb.Webs)
{
try
{
// Do stuff
}
finally
{
subweb.Dispose();
}
}
8.在使用Feature Receiver的时候,SPFeatureReveiverProperties.Feature.Parent 如果是SPWeb或者SPSite对象,不应该被释放:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = properties.Feature.Parent as SPWeb;
// Do stuff
}
以下Event Receiver使用的对象,也不需要释放:
SPWebEventProperties.Web
SPListEventProperties.Web
SPListEventProperties.List.Web
SPItemEventProperties.ListItem.Web
其中的SPItemEventProperties对象是实现了IDisposable接口的,其他的没有实现这个接口。虽然SPItemEventProperties对象实现了IDisposable接口,但是,我们并不知道SharePoint是否正确地释放了它所引用的SPSite对象,所以安全的方法是,我们自己创建SPSite对象,然后显式地释放:
public override void ItemAdded(SPItemEventProperties properties)
{
using (SPSite site = new SPSite(properties.WebUrl))
{
using(SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[properties.ListId];
SPListItem item = list.GetItemById(properties.ListItemId);
// Do stuff
}
}
base.ItemAdded(properties);
}
9.Microsoft.SharePoint.Portal.WebControls.IPersonalPage 有两个属性IPersonalPage.PersonalSite 和IPersonalPage.PersonalWeb,这两个属性不需要释放,以下方法的返回值也是这俩个对象其中之一,一样不需要释放:
UnsecuredLayoutsPage.Web
LayoutsPageBase.Web
SPControl.GetContextWeb()
SPControl.GetContextSite()
SPWebProvisioningProperties.Web
10.有一个特殊的属性:SPWeb.ParentWeb。这个对象可能是SharePoint创建的,也可能是开发人员创建的(这里的创建是指在第一次调用这个属性的时候,SharePoint会为它创建一个SPWeb对象)。如果SPWeb对象是属于SharePoint的,那么SPWeb.ParentWeb也是属于SharePoint的,不需要释放。
11.以下是两个错误释放的例子:
public static SPWeb GetSPWeb(string url)
{
using (var site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
{
return web;
}
}
}
public static SPList GetSPList(string url, Guid listID)
{
SPList list = null;
using (var site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
{
list = web.Lists[listID];
}
}
return list;
}
这样返回的list,其parentWeb已经被释放了,当调用list.ParentWeb的时候,就会报错。
12. 另一个错误释放的例子:点击打开链接