WSSv3 Technical Articles_最佳实践:Windows SharePoint Services对象使用Disposable
摘要:学习在Microsoft .NET Framework下使用Windows SharePoint Services对象模型编写代码的最佳方法,这样可以避免在内存中保留不使用的对象。
Scott Harris, Microsoft Corporation
Mike Ammerlaan, Microsoft Corporation
June 2006
Updated: November 2006
应用:Microsoft Windows SharePoint Services 3.0, Microsoft Windows SharePoint Services 2.0, Microsoft Office SharePoint Server 2007, Microsoft Office SharePoint Portal Server 2003
内容:
Ø 介绍使用Disposable来释放Windows SharePoint Services对象
Ø 编码技巧
Ø SPSite对象
Ø SPWeb对象
Ø 结论
Ø 其他资源
介绍使用Disposable来释放Windows SharePoint Services对象
Microsoft Windows SharePoint Services 3.0对象模型中的对象是为Windows SharePoint Services数据和配置提供服务的接口。开发人员常常调用Windows SharePoint Services的对象模型读写其中存储的数据。
Windows SharePoint Services对象模型中的对象都实现了IDisposable接口。在使用这些对象的时候要谨慎,避免他们长期的驻留在Microsoft .NET Framework内存中。所以,在使用完这些SharePoint对象后,要明确的释放掉它们。在广泛使用SharePoint对象的时候,例如,在SharePoint站点中使用自定义WebPart,如果没有即时处理那些可以被释放掉的对象可能会引起以下的问题:
l Microsoft Windows SharePoint Services的应用程序池频繁回收,特别是在高峰时期。
l 应用程序崩溃,表现为调试器需要的资源溢出。
l Microsoft Internet Information Services(IIS)工作进程占用大量内存。
l 系统和应用程序的性能低下。
这篇文章提供了处理和释放SharePoint对象适当操作的一个指南。
背景
一些Windows SharePoint Services对象,基本的SPSite Class和SPWeb Class对象,创建为受管理的对象。尽管如此,这些对象使用不受管理的代码和内存来完成它们的主要工作。对象中受管理的部分仅占很小的一部分;不受管理的却占很大部分。因为受管理的小部分内容并不会造成垃圾回收器的压力,垃圾回收器也不会及时地释放占用的内存。对象占用的大量不受管理的部分占用的内存会引起之前提到的不正常的情况。在Windows SharePoint Services使应用程序和IDisposable对象一起工作,必须在对象使用完毕后释放对象。不应该依赖垃圾回收器来释放它们占用的内存。
可以使用任何其它的技术来保证对象被回收。在下面的部分将讨论这个问题。
可以利用一些编码技巧来确保对象被释放。这些技术包括在代码中使用以下内容:
l Dispose 方法
l using 子句
l try, catch, finally 块
Dispose vs. Close Method Usage
SPWeb对象和SPSite对象的Dispose和Close方法功能上一样。Dispose方法仅是调用了对象的Close方法。我们推荐调用Dispose方法代替调用Close方法,原因有以下几点:
l SPWeb和SPSite对象实现了IDisposable接口,并且标准.NET Framework进程调用Dispose方法来释放与对象关联的所有内存资源。
l 将来代码发布也可以保证被正确调用,而不会长时间占用资源。
using Clause
使用Microsoft Visual C#的using子句,它实现了IDisposable接口,它可以自动释放SharePoint对象。
下面的代码提供了一个例子:
String str;
using (SPSite oSPsite = new SPSite("http://server"))
{
using (SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}
try/catch/finally Blocks
下面是使用try/catch/finally Blocks的推荐写法。当使用try块时,其中很重要的一点就是添加finally块来确保所有的对象都被适当的释放掉了。
任何代码被包含在try{ }catch{ }块中需要带有finally块,用这个来保证对象被释放。使用SharePoint对象的时候try{ }catch{ }可以存在功能以外的地方。在那些情况下,就需要在方法里考虑使用try/catch/finally直接将使用的SharePoint对象包进去。
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();
}
调用Response.Redirect将不会执行finally块的内容。因此,在发生任何重定向或跳转之前,都需要释放掉对象。例如,代码里面必须要进行重定向,可以按照如下方法进行处理:
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
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();
}
推荐减少长期的对象保持
为了减少Windows SharePoint Services对象的长期保持,提供以下几种推荐的编码方法:
1.如果为了操作创建了一个新的对象,那么这个创建的应用程序需要对其进行释放。
第一种方式
SPSite oSPSite = new SPSite("http://server");
... additional processing on SPSite ...
oSPSite.Dispose();
第二种方式
using (SPSite oSPSite = new SPSite("http://server"))
{
... additional processing on SPSite ...
}
2.SharePoint对象返回了其他的SPWeb对象(例如:SPSite.OpenWeb)创建的新对象,需要对其进行释放。
第一种方式:
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();
第二种方式:
String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
... additional processing on SPWeb ...
}
}
3.SPSite.RootWeb属性和SPWeb.ParentWeb属性创建了新的对象并将他们赋给本地成员变量。每次成功访问并使用它们都将他们放到一个本地的成员变量中。这样,可以随时对他们进行调用,而不需要创建很多对象。尽管如此,还是需要在使用完这些属性的时候调用Dispose方法释放它们。在下面的例子中,可以对RootWeb和ParentWeb使用同样的处理方法。
第一种方法:
String str;
SPSite oSPSite = new SPSite("http://server");
str = oSPSite.RootWeb.Title;
str = oSPSite.RootWeb.Url;
... additional processing on RootWeb ...
oSPSite.RootWeb.Dispose();
oSPSite.Dispose();
第二种方法:
String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
str = oSPSite.RootWeb.Title;
str = oSPSite.RootWeb.Url;
... additional processing on RootWeb ...
oSPSite.RootWeb.Dispose();
}
第三种方法:
String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oRootWeb = oSPSite.RootWeb)
{
str = oRootWeb.Title;
str = oRootWeb.Url;
… additional processing on RootWeb …
}
}
SPSite对象
本部分将描述在何种情况下需要对SPSite对象进行Dispose。
一般情况下,任何一个应用都会调用SPSite的构造器来创建一个对象,在使用完的时候我们就需要调用SPSite.Dispose方法来释放资源。如果是从SPControl.GetContextSite获取到的SPSite对象,调用的应用将不会释放掉对象占用的资源。因为SPWeb和SPSite对象在这种情况下被保存在了一个内部的列表中,如果释放了它们,将会引起SharePoint对象模型的一些其他错误。内部处理中,会在页面完成时用Windows SharePoint Services的枚举来枚举列表中的所有对象并释放它们。
SPSiteCollection Class
这一部分将描述在何种情况下需要对使用的SPSiteCollection对象的方法、属性、操作进行Close。
SPSiteCollection.Add Method
SPSiteCollection.Add将创建并返回一个SPSite对象。在使用完这个方法创建的对象后需要对其进行释放。
第一种方法:
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
SPSite oSPSite = aSites.Add( ... );
... Process the site info ...
oSPSite.Dispose();
oSPGlobalAdmin.Dispose();
第二种方法:
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
using(SPSite oSPSite = aSites.Add( ... ))
{
... Process the site info ...
}
oSPGlobalAdmin.Dispose();
SPSiteCollection[] Index Operator
每次访问SPSiteCollection[]的其中一个索引的内容就会返回一个SPSite对象。尽管这个SPSite对象被访问过了,但还是创建出了一个实例。下面的代码是不适当的释放方法。
第一种不适当的方法:
int i;
SPSite oSPSite;
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
for (i = 0; i < aSites.Count; i++)
{
oSPSite = aSites[i];
BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
}
oSPGlobalAdmin.Dispose();
第二种不适当的方法:
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
foreach (SPSite oSPSite in aSites)
{
BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
}
oSPGlobalAdmin.Dispose();
推荐的方法里面修正了其中的问题,在每次循环结束之前释放掉创建的实例。
第一种推荐的方法:
int i;
SPSite oSPSite;
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
for (i = 0; i < aSites.Count; i++)
{
oSPSite = aSites[i];
BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
oSPSite.Dispose();
}
oSPGlobalAdmin.Dispose();
第二种推荐的方法:
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
foreach (SPSite oSPSite in aSites)
{
BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
oSPSite.Dispose();
}
oSPGlobalAdmin.Dispose();
第三种推荐的方法:
int i;
SPGlobalAdmin oSPGlobalAdmin = new SPGlobalAdmin();
SPSiteCollection aSites = oSPGlobalAdmin.VirtualServers[0].Sites;
for (i = 0; i < aSites.Count; i++)
{
using (SPSite oSPSite = aSites[i])
{
BuildTableRow(oDisplayTable, "Site", oSPSite.Url);
}
}
oSPGlobalAdmin.Dispose();
SPSite.AllWebs Property (SPWebCollection)
这一部分将描述在何种情况下需要对使用的SPSite.AllWebs属性中的SPWeb对象的方法、属性、操作进行Close。
SPSites.AllWebs.Add Method
SPSite.AllWebs.Add方法创建并返回一个SPWeb对象。在使用完后需要对这个方法创建出的对象进行释放。
第一种方法:
SPWeb oSPWeb;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.AllWebs.Add( ... );
... Process the SPWeb info ...
oSPWeb.Dispose();
第二种方法:
SPSite oSPSite = SPControl.GetContextSite(Context);
using(SPWeb oSPWeb = oSPSite.AllWebs.Add( ... ))
{
... Process the SPWeb info ...
}
注意:
如果SPSite对象是从GetContextSite()方法获取的,则不需要手工对其进行释放。
SPSite.AllWebs [ ] Index Operator
每次访问SPSite.AllWebs []的其中一个索引的内容就会返回一个SPWeb对象。如果这个属性没有被关闭,下面的代码会将创建出来的SPWeb对象留在.NET Framework的垃圾回收器中等待自动回收,这就就不能马上释放资源。
第一种不适当的方法:
int i;
SPWeb oSPWeb;
SPSite oSPSite = SPControl.GetContextSite(Context);
for (i = 0; i < oSPSite.AllWebs.Count; i++)
{
oSPWeb = oSPSite.AllWebs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
}
第二种不适当的方法:
SPSite oSPSite = SPControl.GetContextSite(Context);
foreach (SPWeb oSPWeb in oSPSite.AllWebs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
}
推荐的方法里面修正了其中的问题,在每次循环结束之前释放掉创建的实例。
第一种推荐的方法:
int i;
SPWeb oSPWeb;
SPSite oSPSite = SPControl.GetContextSite(Context);
for (i = 0; i < oSPSite.AllWebs.Count; i++)
{
oSPWeb = oSPSite.AllWebs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
oSPWeb.Dispose();
}
第二种推荐的方法:
SPSite oSPSite = SPControl.GetContextSite(Context);
foreach (SPWeb oSPWeb in oSPSite.AllWebs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
oSPWeb.Dispose();
}
第三种推荐的方法:
int i;
SPWeb oSPWeb;
SPSite oSPSite = SPControl.GetContextSite(Context);
for (i = 0; i < oSPSite.AllWebs.Count; i++)
{
using (oSPWeb = oSPSite.AllWebs[i])
{
BuildTableRow(oDisplayTable, "Web", oSPWeb.Title);
}
}
SPSite.OpenWeb and SPSite. SelfServiceCreateSite Methods
SPSite.OpenWeb和SPSite. SelfServiceCreateSite方法会创建一个SPWeb对象并返回。在SPSite类中,这个新建的对象并不存储在SPSite对象中也不会被自动释放。因此,需要通过以下方法来释放它们。
第一种方法:
SPSite oSPSite = new SPSite("http://Server");
SPWeb oSPWeb = oSPSite.OpenWeb(..);
... additional processing ...
oSPWeb.Dispose();
oSPSite.Dispose();
第二种方法:
using(SPSite oSPSite = new SPSite("http://Server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb(..))
{
... additional processing ...
}
}
SPSite.LockIssue, SPSite.Owner, and SPSite.SecondaryContact Properties
以下这些属性引用自顶级站点的数据并使用SPSite.RootWeb属性。
如果使用了任何其他的属性,也需要在SPSite.RootWeb属性上调用Dispose方法。
第一种方法:
String str;
SPSite oSPSite = new SPSite("http://server");
str = oSPSite.LockIssue;
oSPSite.RootWeb.Dispose();
oSPSite.Dispose();
第二种方法:
String str;
using (SPSite oSPSite = new SPSite("http://server"))
{
str = oSPSite.Owner;
oSPSite.RootWeb.Dispose();
}
SPSite.RootWeb Property
如果第一次访问SPSite.RootWeb,这个属性将判断这个成员变量的引用是否是一个空值。如果这个值是空值,将会调用SPSite.OpenWeb方法创建一个新的SPWeb对象并将这个对象赋给这个成员变量。下次再访问SPSite.RootWeb属性时,就直接将这个值返回。
需要注意,如果使用了SPSite.RootWeb,需要在释放SPSite之前释放SPSite.RootWeb。
第一种方法:
String str;
SPSite oSPSite = new SPSite("http://server");
str = oSPSite.RootWeb.Title;
... additional processing ...
oSPSite.RootWeb.Dispose();
oSPSite.Dispose();
第二种方法:
String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
str = oSPSite.RootWeb.Title;
... additional processing ...
oSPSite.RootWeb.Dispose();
}
第三种方法:
String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oRootWeb = oSPSite.RootWeb)
{
str = oRootWeb.Title;
... additional processing ...
}
}
SPWeb对象
这部分将描述SPWeb对象需要释放的一些情况。
SPWeb.ParentWeb Property
如果第一次访问SPSite.ParentWeb,这个属性将判断这个成员变量的引用是否是一个空值。如果这个值是空值,将会判断它的ParentWeb是否存在,如果存在将调用OpenWeb方法来创建出这个对象并存储在成员变量中,然后返回这个对象。下次再访问SPSite.ParentWeb属性时,就直接将这个值返回。
需要注意,如果使用了SPSite.ParentWeb,需要在释放SPWeb之前释放SPSite.ParentWeb。
第一种方法:
String str;
SPSite oSPSite = new SPSite("http://server");
SPWeb oSPWeb, oSPWebParent;
oSPWeb = oSPSite.OpenWeb();
oSPWebParent = oSPWeb.ParentWeb;
if (oSPWebParent != null)
{
... additional processing ...
}
if (oSPWebParent != null)
oSPWebParent.Dispose();
oSPWeb.Dispose();
oSPSite.Dispose();
第二种方法:
String str;
SPWeb oSPWeb, oSPWebParent;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
oSPWebParent = oSPWeb.ParentWeb;
if(oSPWebParent != null)
{
... additional processing ...
}
If(oSPWebParent != null)
oSPWebParent.Dispose();
}
}
SPWeb.Webs Property (SPWebCollection)
这一部分将描述在何种情况下需要对使用的SPWeb.Webs属性中的SPWeb对象的方法、属性、操作进行Close。
SPWeb.Webs.Add
SPWeb.Webs.Add将创建并返回一个SPWeb对象。在使用完这个方法创建的对象后需要对其进行释放。
第一种方法:
SPWeb oSPWeb
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPSWeb = oSPSite.AllWebs.Add( ... );
... Process the SPWeb info ...
oSPWeb.Dispose();
第二种方法:
SPSite oSPSite = SPControl.GetContextSite(Context);
using(SPWeb oSPSWeb = oSPSite.AllWebs.Add( ... ))
{
... Process the SPWeb info ...
}
注意:
如果SPSite对象是从GetContextSite()方法获取的,则不需要手工对其进行释放。
SPWeb.Webs[ ] Index Operator
每次访问SPWeb.Webs []的其中一个索引的内容就会返回一个SPWeb对象。尽管这个SPWeb对象被访问过了,但还是创建出了一个实例。下面的代码是不适当的释放方法。
第一种不适当的方法:
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
for (i = 0; i < oSPWeb.Webs.Count; i++)
{
oSPWeb2 = oSPWeb.Webs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
第二种不适当的方法:
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
foreach (SPWeb oSPWeb2 in oSPWebe.Webs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
推荐的方法里面修正了其中的问题,在每次循环结束之前释放掉创建的实例。
第一种推荐的方法:
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
for (i = 0; i < oSPWeb.Webs.Count; i++)
{
oSPWeb2 = oSPWeb.Webs[i];
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
oSPWeb2.Dispose();
}
oSPWeb.Dispose();
第二种推荐的方法:
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
oSPWeb = oSPSite.OpenWeb();
foreach (SPWeb oSPWeb2 in oSPWeb.Webs)
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
oSPWeb2.Dispose();
}
oSPWeb.Dispose();
第三种推荐的方法:
int i;
SPWeb oSPWeb, oSPWeb2;
SPSite oSPSite = SPControl.GetContextSite(Context);
using (oSPWeb = oSPSite.OpenWeb())
{
for (i = 0; i < oSPWeb.Webs.Count; i++)
{
using (oSPWeb2 = oSPWeb.Webs[i])
{
BuildTableRow(oDisplayTable, "Web", oSPWeb2.Title);
}
}
}
其他需要Disposal的对象
这部分将介绍其他SharePoint对象什么时候需要调用Dispose方法。
Microsoft.SharePoint.Portal.SiteData.Area.Web Property
访问Area.Web属性时将返回一个SPWeb对象。每次使用这个对象之后都要调用Dispose来释放资源。
不适当的写法:
String str;
Area oArea = AreaManager.GetArea(PortalContext.Current, new Guid(AreaGiud));
str = oArea.Web.Title; // Web is not reclaimed
str = oArea.Web.Url; // Web is not reclaimed
第一种适当的写法:
String str;
Area oArea = AreaManager.GetArea(PortalContext.Current, new Guid(AreaGiud));
SPWeb oSPWeb = oArea.Web;
str = oSPweb.Title;
str = oSPWeb.Url;
...
oSPWeb.Dispose();
第二种适当的写法:
String str;
Area oArea = AreaManager.GetArea(PortalContext.Current, new Guid(AreaGiud));
using (SPWeb oSPWeb = oArea.Web)
{
str = oSPweb.Title;
str = oSPWeb.Url;
}
SPControl.GetContextSite and SPControl.GetContextWeb Methods
如果对象是从SharePoint上下文对象中获得,应用程序则不需要调用Dispose方法来释放对象。如果对它们进行释放,将会引起SharePoint对象模型的异常或操作失败。它们被存在SPSite和SPWeb的一张内部列表中。在页面执行完毕后将会对它们一一进行释放。
但仍需要对一些从以下对象创建出的对象进行释放,如果是从SPControl.GetContextSite方法获取到SPSite对象,可以从这个对象打开并获取到Web站点对象。
不适当的写法:
SPSite oSPSite = SPControl.GetContextSite(..);
... additional processing ...
oSPSite.Dispose();
// Could cause problems in the SharePoint object model
第一种适当的写法:
SPSite oSPSite = SPControl.GetContextSite(..);
SPWeb oSPWeb = oSPSite.OpenWeb(..);
... additional processing ...
oSPWeb.Dispose();
第二种适当的写法:
SPSite oSPSite = SPControl.GetContextSite(..);
using(SPWeb oSPWeb = oSPsite.OpenWeb())
{
... additional processing ...
}
WebPartPage.RootWeb Property
WebPartPage.RootWeb的调用和SPSite.RootWeb类似。判断这个成员变量的引用是否是一个空值。如果这个值是空值,将调用SPSite.OpenWen方法创建并返回SPWeb对象;新建的SPWeb对象存储在成员变量中,或者,如果SPWeb是一个顶级站点,将存放到其他的指到WebPartPage.Web的成员变量中。
调用它的应用仅仅在WebPartPage.IsRootWeb返回一个真值时需要释放WebPartPage.RootWeb属性。
推荐方法:
String str;
WebPartPage oWebPartPage = new WebPartPage();
str = oWebPartPage.RootWeb.Title;
... additional processing ...
if(oWebPartPage.Web.IsRootWeb)
oWebPartPage.Dispose();
很多SharePoint对象实现了IDisposable接口,所以你必须小心使用SharePoint对象模型来避免将它们驻留在内存中。这篇文章中描述的关于它们Dispose的指南,可以帮助你保证Windows SharePoint Services自定义的可靠性。
致谢
We would like to thank the following people for their input and guidance in creating this document:
l Keith Richie, Microsoft Corporation
l Rob Anderson, Microsoft Corporation
l Steve Sheppard, Microsoft Corporation
l Chris Gideon, Microsoft Corporation
l Kelvin Lo, Microsoft Corporation
l Rashid Aga, Microsoft Corporation
For more information on this and other SharePoint issues, see Keith Richie's blog.
Microsoft Windows SharePoint Services Developer Center
Microsoft Office Developer Center: SharePoint Server 2007 Developer Portal
原文地址:Best Practices: Using Disposable Windows SharePoint Services Objects