• SharePoint 2010开发最佳实践指南(一)


    摘要:

    本文基于SharePoint SDK文档,总结了开发者最佳实践,以对SharePoint2010开发提供指南。本指南覆盖2007以及2010两个版本,包括对SPSite和SPWeb对象的使用指南,系统Template文件夹下部署内容时的命名规范,事件处理器内SPWeb, SPSite对象使用、大文件夹以及大列表的处理、对象缓存以及代码优化的一些例子。

    避免使用构造不必要的SPWeb和SPSite对象

    1. SPWeb和SPSite会占用大量的内存,开发时尽量避免构造新的SPWeb和SPSite对象,特别是只是为了取得对SPWebApplication的引用等情况,为了取得SPWebApplication,可以调用SPWebApplication.Lookup(uri)来取得引用,接下类也可以通过webApplication.Farm来取得对farm的引用,进一步如果知道内容数据库的索引值,可以通过webApplication.ContentDatabase[index]来取得对内容数据库的引用。

    SPWebApplication webApplication = SPWebApplication.Lookup(new Uri("http://localhost/");
    SPFarm farm = webApplication.Farm;
    SPContentDatabase content = webApplication.ContentDatabases[0];
    

    2. 如果一定要构造SPWeb和SPSite对象,务必注意使用完后对象的释放,可以使用以下三种技术:

        Dispose方法

        using

        try, catch, finally

        使用using如下:

    String str;
    using(SPSite oSPsite = new SPSite("http://server"))
    {
      using(SPWeb oSPWeb = oSPSite.OpenWeb())
       {
           str = oSPWeb.Title;
           str = oSPWeb.Url;
       }
    }  
    
    但是在使用的时候一定要特别注意,避免释放了不该释放的内容,比如:
    using( SPWeb web = SPControl.GetContextWeb(HttpContext.Current)) { ... }
    SPContext由SharePoint框架来进行维护,不应该释放,SPContext.Site, SPContext.Current.Site, SPContext.Web, SPContext.Current.Web同理也不能释放。
    try, catch, finally本质和using是一样,对实现了IDisposal接口的对象,.NET运行时环境会自动将using转换成try, catch, finally.
    String str;
    SPSite oSPSite = null;
    SPWeb oSPWeb = null;
    
    try
    {
       oSPSite = new SPSite("http://server");
       oSPWeb = oSPSite.OpenWeb(..);
    
       str = oSPWeb.Title;
    }
    catch(Exception e)
    {
       //Handle exception, log exception, etc.
    }
    finally
    {
       if (oSPWeb != null)
         oSPWeb.Dispose();
    
       if (oSPSite != null)
          oSPSite.Dispose();
    }
    

    页面重定向时候的资源释放:如下例,在Response.Redirect调用时会生成ThreadAbortedException异常,而之后finally会被执行,此时会导致线程的异常,无法保证资源一定会得到释放,因此在任何Response.Redirect调用之前一定要确保SPSite,SPWeb对象的释放

    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();
    }
    

    以上问题也适用于using。

    不要创建静态的SPWeb和SPSite对象

    调用SPSiteCollection.Add方法创建返回的SPSite对象需要释放;

    通过SPSiteColleciton的索引器SPSiteColleciton[]返回的SPSite对象需要释放;

    在SPSiteCollection集合中进行foreach是的SPSite对象需要释放;

    以下是推荐的做法:

    void SPSiteCollectionForEachNoLeak()
    {
        using (SPSite siteCollectionOuter = new SPSite("http://moss"))
        {
            SPWebApplication webApp = siteCollectionOuter.WebApplication;
            SPSiteCollection siteCollections = webApp.Sites;
    
            foreach (SPSite siteCollectionInner in siteCollections)
            {
                try
                {
                    // ...
                }
                finally
                {
                    if(siteCollectionInner != null)
                        siteCollectionInner.Dispose();
                }
            }
        } // SPSite object siteCollectionOuter.Dispose() automatically called.
    }
    

    通过SPSite.AllWebs.Add返回的SPWeb对象需要释放;

    通过SPWebColleciton.Add返回的SPWeb对象需要释放;

    通过SPSite.AllWebs[]索引器返回的SPWeb对象需要释放;

    通过foreach循环SPWebColleciton的SPWeb对象需要释放;

    通过OpenWeb打开的SPWeb对象需要释放;

    通过SPSite.SelfServiceCreateSite创建的SPWeb对象需要释放;

    SPSite.RootWeb不需要进行释放;

    通过Microsoft.Office.Server.UserProfiles.PersonalSite返回的SPSite对象需要释放;

    推荐的开发方式如下:
    void PersonalSiteNoLeak()
    {
        // Open a site collection
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
            UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
            UserProfile profile = profileManager.GetUserProfile("domain\\username");
            using (SPSite personalSite = profile.PersonalSite)
            {
                // ...
            }
        }
    }
    
    此处可以通过ProfileLoader省去构造新的SPSite以提高性能
    UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
    using (SPSite personalSite = myProfile.PersonalSite)
    {
         // ...
    }
    

    特别,如果为MySite创建web部件,可以使用PersonalSite而不用释放:

    IPersonalPage currentMySitePage = this.Page as IPersonalPage; 
    if (currentMySitePage != null && !currentMySitePage.IsProfileError) 
    { 
        SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak. // ... 
    }
    
    

    通过GetContextSite返回的SPSite不需要释放

    void SPControlBADPractice()
    {
        SPSite siteCollection = SPControl.GetContextSite(Context);
        //siteCollection.Dispose();   不要释放
        SPWeb web = SPControl.GetContextWeb(Context);
        //web.Dispose();  不要释放
    }
    
    SPLimitedWebPartManager含有内部对SPWeb的引用,需要释放
    void SPLimitedWebPartManagerLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
            using (SPWeb web = siteCollection.OpenWeb())
            {
                SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
                SPLimitedWebPartManager webPartManager =
                    page.GetLimitedWebPartManager(PersonalizationScope.Shared);
                    webPartManaber.Web.Dispose();
            } // SPWeb object web.Dispose() automatically called.
        }  // SPSite object siteCollection.Dispose() automatically called. 
    }
    
    Microsoft.SharePoint.Publishing.PublishingWeb(SharePoint2007 only)

    PublishingWeb.GetPublishingWebs会返回PublishingWebCollection,foreach时候需要调用close方法释放每一个对象:

    void PublishingWebCollectionNoLeak()
    {
        using (SPSite siteCollection = new SPSite("http://moss"))
        {
            using (SPWeb web = siteCollection.OpenWeb())
            {
                // Passing in SPWeb object that you own, no dispose needed on
                // outerPubWeb.
                PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);
                PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();
                foreach (PublishingWeb innerPubWeb in pubWebCollection)
                {
                    try
                    {
                        // ...
                    }
                    finally
                    {
                        if(innerPubWeb != null)
                            innerPubWeb.Close();
                    }
                }
            }  // SPWeb object web.Dispose() automatically called.
        } // SPSite object siteCollection.Dispose() automatically called.
    }
    

    同样,调用PublishingWebCollection.Add返回的PublishingWeb也需要释放;

    3.在事件处理器里可以使用以下方法避免生成新的SPSite或者SPWeb

    // Retrieve SPWeb and SPListItem from SPItemEventProperties instead of 
    // from a new instance of SPSite. 
    SPWeb web = properties.OpenWeb();//此处通过properties.OpenWeb()返回的SPWeb不用释放; 
    // Operate on the SPWeb object. 
    SPListItem item = properties.ListItem; 
    // Operate on an item.
    
    

    文件名限制:

    在实战中如果需要部署文件夹或者文件到%ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE目录,出于安全考虑,SharePoint Foundation只能够读取文件名有ASCII字符、数字、下划线、句号、破折号(dashed)组成的名字,特别是文件名不能包括两个连续的句号,例如,以下是允许的文件名:

    AllItems.aspx

    Dept_1234.doc

    Long.Name.With.Dots.txt

    以下是不允许的名字:

    Cæsar.wav

    File Name With Spaces.avi

    Wow...ThisIsBad.rtf

    揵.htm

    大文件夹、大列表的处理

    不要使用SPList.Items,因为这个调用会返回所有子文件夹下的所有记录,使用以下方法替代:

    添加记录:使用SPList.AddItem,不使用SPList.Items.Add;

    查询记录:使用SPList.GetItemById,不适用SPList.Items.GetItemById;

    返回列表所有记录:使用SPList.GetItems(SPQuery query)而不是SPList.Items,根据需要使用条件过滤,仅仅挑选必要的字段返回,如果返回结果超过2000条,采用分页技术:

    SPQuery query = new SPQuery();
    SPListItemCollection spListItems ;  string lastItemIdOnPage = null; // Page position.
    int itemCount = 2000   while (itemCount == 2000)
    {
        // Include only the fields you will use.
        query.ViewFields = "<FieldRef Name=\"ID\"/><FieldRef Name=\"ContentTypeId\"/>";   query.RowLimit = 2000; // Only select the top 2000.
        // Include items in a subfolder (if necessary).
        query.ViewAttributes = "Scope=\"Recursive\"";
        StringBuilder sb = new StringBuilder();
        // To make the query order by ID and stop scanning the table, specify the OrderBy override attribute.
        sb.Append("<OrderBy Override=\"TRUE\"><FieldRef Name=\"ID\"/></OrderBy>");
        //.. Append more text as necessary ..
        query.Query = sb.ToString();   // Get 2,000 more items.   SPListItemCollectionPosition pos = new SPListItemCollectionPosition(lastItemIdOnPage);
        query.ListItemCollectionPosition = pos; //Page info.
        spListItems = spList.GetItems(query);
        lastItemIdOnPage = spListItems.ListItemCollectionPosition.PagingInfo;
        // Code to enumerate the spListItems.
        // If itemCount <2000, finish the enumeration.
        itemCount = spListItems.Count;
    
    }
    

    分页显示:

    SPWeb oWebsite = SPContext.Current.Web;
    SPList oList = oWebsite.Lists["Announcements"];
    
    SPQuery oQuery = new SPQuery();
    oQuery.RowLimit = 10;
    int intIndex = 1;
    
    do
    {
        Response.Write("<BR>Page: " + intIndex + "<BR>");
        SPListItemCollection collListItems = oList.GetItems(oQuery);
    
        foreach (SPListItem oListItem in collListItems)
        {
            Response.Write(SPEncode.HtmlEncode(oListItem["Title"].ToString()) +"<BR>");
        }
    
        oQuery.ListItemCollectionPosition = collListItems.ListItemCollectionPosition;
        intIndex++;
    } while (oQuery.ListItemCollectionPosition != null);
    

    性能差不推荐使用的API

    性能更好的推荐使用的API

    SPList.Items.Count

    SPList.ItemCount

    SPList.Items.XmlDataSchema

    创建SPQuery,仅仅返回需要的数据

    SPList.Items.NumberOfFields

    创建SPQuery,指定ViewFields,仅仅返回需要的数据

    SPList.Items[System.Guid]

    SPList.GetItemByUniqueId(System.Guid)

    SPList.Items[System.Int32]

    SPList.GetItemById(System.Int32)

    SPList.Items.GetItemById(System.Int32)

    SPList.GetItemById(System.Int32)

    SPList.Items.ReorderItems(System.Boolean[],System.Int32[],System.Int32)

    使用SPQuery分页

    SPList.Items.ListItemCollectionPosition

    ContentIterator.ProcessListItems(SPList, ContentIterator.ItemProcessor, ContentIterator.ItemProcessorErrorCallout) (Microsoft SharePoint Server 2010 only)

    参考:

    释放检查工具SPDisposeCheck: http://code.msdn.microsoft.com/SPDisposeCheck

    http://msdn.microsoft.com/en-us/library/aa973248(office.12).aspx

    http://msdn.microsoft.com/en-us/library/bb687949%28office.12%29.aspx

  • 相关阅读:
    嵌套类型返回错误解决办法(如迭代器的设计)
    UITableView的多选删除模式
    UITableView的编辑模式
    指针和引用初理解
    strstr()函数实现
    一句话的单词倒置
    字符串过滤程
    strcpy函数
    二叉排序树(Binary Sort Tree)
    二叉树插入操作
  • 原文地址:https://www.cnblogs.com/johnsonwong/p/2053856.html
Copyright © 2020-2023  润新知