学习:使用SPSite和SPWeb对象要注意的事项(转)

 

在使用SPSite对象时容易发生内存泄漏!造成内存泄漏的原因是没有正确地关闭SPSite对象,请大家Review一下代码,及时修正!
项目中,对系统进行压力测试时,出现了大量的异常信息,类似如下面的:
Microsoft.SharePoint.SPException: Attempted to make calls on more than one thread in single threaded mode. (Exception from HRESULT: 0x80010102 (RPC_E_ATTEMPTED_MULTITHREAD)) ---> System.Runtime.InteropServices.COMException (0x80010102): Attempted to make calls on more than one thread in single threaded mode. (Exception from HRESULT: 0x80010102 (RPC_E_ATTEMPTED_MULTITHREAD))
   at Microsoft.SharePoint.Library.SPRequestInternalClass.SetVar(String bstrUrl, String bstrName, String bstrValue)
   at Microsoft.SharePoint.Library.SPRequest.SetVar(String bstrUrl, String bstrName, String bstrValue)
   --- End of inner exception stack trace ---
   at Microsoft.SharePoint.Library.SPRequest.SetVar(String bstrUrl, String bstrName, String bstrValue)
   at Microsoft.SharePoint.SPListItemCollection.EnsureListItemsData()
   at Microsoft.SharePoint.SPListItemCollection.Undirty()
   at Microsoft.SharePoint.SPBaseCollection.System.Collections.IEnumerable.GetEnumerator()
   at OA2.MossAccessBlock.Portal.NewsHelper.GetAreaNewsDom(String subType, String area, Int32 pictureNum, Int32 newsNum) in D:\OA2_BuildVersion\MossAccessBlock\Portal\NewsHelper.cs:line 191


上面的异常的意思是说:在单线程模式下尝试进行多线程呼叫。在压力测试的过程中,出现了诸如“服务器内存不足”、“连接不到数据库服务器”、“找不到网站”等一些在单用户(用户访问量较小的情况下)使用的时候不会出现的异常,而在这些异常出现后的一段时间内,服务器会给出HTTP500的响应(MOSS服务器挂了),幸运的话过一段时间等压力降下来后服务器会自动地活过来,这现象比较神奇;倒霉的话应用程序池会自动停止,但我们一般都是RESET IIS就了事了,就是因为这样,如果没有做大量的压力测试,不容易给人发现。
经过大量的异常信息对比,我们发现异常中的代码都会与创建SPSite对象时相关联的,经过翻查SDK,有这样一段话:
If you create your own SPSite object, you can use the Dispose method or the Close method to close the object. However, if you have a reference to a shared resource, such as when the object is provided by the SPControl.GetContextSite method in a Web Part, do not use either method to close the object. In scenarios where you have a reference to a shared resource, instead let Windows SharePoint Services or your portal application manage the object. Using either method on a shared resource causes an access violation error to occur.

我们在使用SPSite的时候有两种途径能获得该对象:1. New SPSite(siteURL); 2. SPControl.GetContextSite(this.Context)
SDK的意思是说我们在使用第一种方法获得SPSite时,可以(应该说是必须)使用Close()方法或Dispose()方法关闭该对象;而使用GetContextSite()获得的SPSite对象一定不能使用那两个方法关闭该对象,使用GetContextSite()等类似的方法,如从Current对象出获得的SPSite对象,都不能进行了关闭操作,因为那是一个共享的对象,如强行关闭的话程序会出错(验证过的确会出错,当初所有代码的SPSite都没有关闭就是因为在关闭时会导致程序出错,所以没有关闭,但并没注意是用哪种方法获得的SPSite)。
很明显,共享的对象在理论一是性能是优于独立创建的一个新的对象,起码就会少创建对象的时间等操作,所以在只有一个网站集的系统(像巴陵石化项目)中,请尽量使用GetContextSite()方法获得网站集对象SPSite,以减少出错的机会及优化程序的性能。
而SPSite.Close()和SPSite.Dispose()有什么不同呢?以前看过.Net的书,Close()仅仅是释放当前对象的资源,而Dispose()在释放当前对象资源时还会调用其成员对象的Dispose()方法,以释放成员对象的资源,所以Dispose()比Close()释放得更彻底,在找这个线程异常的原因时,我也分别对Close()方法和Dispose()方法做了压力测试,使用Close()方法后,抛出线程异常的数量比没有使用任何方法时明显减少了,但还是会有;使用Dispose()方法后,就再也没有抛出该异常。我想原因是SPSite对象内有个SPWebCollections对象,包含了网站集下所有的子站点对象SPWeb,SPWeb和SPSite一样,在SDK里均有如SPSite一样的说明,所以使用SPSite.Close()方法还是不够彻底,除非你自己手工去Dispose所有的SPWeb对象。
综上所述,在使用New方法独立创建一个SPSite对象时,得写成类似下面形式:
using (SPSite site = new SPSite(siteURL or siteGUID))
    {
       // your code here
    }

           SPSite site = null;
           try
           {
                    site = new SPSite(siteURL or siteGUID);
                    // your code here
           }
           catch{}
           finially
           {
                    site.Dispose();
           }
           
最后说说有关MOSS里的特权代码,就是以系统帐号身份运行某一程序段,看下面代码:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite site = new SPSite(web.Site.ID))
    {
       // do things assuming the permission of the "system account"
    }
});
在使用特权代码时,必须注意在特权代码中的SPSite和SPWeb 对象必须是独立创建的对象,不能使用共享对象,否则也会抛异常,就是说不能写成像下面的代码:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite site = SPControl.GetContextSite(this.Context))
    {
       // do things assuming the permission of the "system account"
    }
});

文章来源:http://bbs.winos.cn/viewthread.php?tid=41204

DO NOT

Microsoft.SharePoint.WebControls.SPControl

void SPControlBADPractice()

{

    SPSite siteCollection = SPControl.GetContextSite(Context);

    siteCollection.Dispose();   // DO NOT DO THIS

    SPWeb web = SPControl.GetContextWeb(Context);

    web.Dispose(); // DO NOT DO THIS

}

 

void SPControlBestPractice()

{

    SPSite siteCollection = SPControl.GetContextSite(Context);

    SPWeb web = SPControl.GetContextWeb(Context);

    // Do NOT call Dispose()

}

Microsoft.SharePoint.SPContext

void SPContextBADPractice()

{

    SPSite siteCollection = SPContext.Current.Site;

    siteCollection.Dispose(); // DO NOT DO THIS

    SPWeb web = SPContext.Current.Web;

    web.Dispose(); // DO NOT DO THIS

}

 

void SPContextBestPractice()

{

    SPSite siteCollection = SPContext.Current.Site;

    SPWeb web = SPContext.Current.Web;

    // Do NOT call Dispose()

}

Interesting SPSite leak pattern

Today I came across a very interesting coding pattern:

public void enumSiteCollection(SPSiteCollection spSites, int min, int max)

{

    for (int i = min; i<max; i++)

    {

        if (spSites[i] != null)

        {
            DoSomething(spSites[i]);

            spSites[i].Dispose();

        }

    }

}

On a first look the code look ok, right? Ok, no try/catch blog and so on but for normal operations it looks as if the SPSite objects used from the SPSiteCollection object are correctly disposed, right?

The problem here is hidden in the internal implementation of the indexer of the SPSiteCollection object. What happens under the hood is that for every single use of spSites[i] a new independent SPSite object is created.

That means the code above creates 3 SPSite objects - but only disposes one.

A correct implementation of the code would look like this:

public void enumSiteCollection(SPSiteCollection spSites, int min, int max)

{

    for (int i = min; i<max; i++)

    {

        SPSite site = spSites[i];

        if (site != null)

        {
            DoSomething(site);

            site.Dispose();

        }

    }

}

 

That will ensure that only one single SPSite object is created, reused and finally disposed.

 

Ok, completely correct it should be something like this:

 

public void enumSiteCollection(SPSiteCollection spSites, int min, int max)

{
    SPSite site = null;

    for (int i = min; i<max; i++)

    {

        try

        {

            site = spSites[i];

            if (site != null)

            {
                DoSomething(site);

            }

        }

        finally

        {

            if (site != null)

            {
               site.Dispose();

            }

        }

    }

}

 

or this:

 

public void enumSiteCollection(SPSiteCollection spSites, int min, int max)

{

    for (int i = min; i<max; i++)

    {

        using (SPSite site = spSites[i])

        {

            if (site != null)

            {
                DoSomething(site);

            }

        }

    }

}

 

 

Here you can find more coding patterns which need to be avoided:

 

http://blogs.msdn.com/rogerla/archive/2008/02/12/sharepoint-2007-and-wss-3-0-dispose-patterns-by-example.aspx
http://msdn.microsoft.com/en-us/library/aa973248.aspx#sharepointobjmodel__spwebobjects
有关SPSite和SPWeb对象正确的编码方式可以查看MSDN的网站,里面有详细的介绍。

原文地址:https://www.cnblogs.com/LeimOO/p/1413192.html