xxl520 发表于 2015-9-29 07:36:41

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来取得对内容数据库的引用。








view sourceprint?







1
SPWebApplication webApplication = SPWebApplication.Lookup(new Uri("http://localhost/");





2
SPFarm farm = webApplication.Farm;





3
SPContentDatabase content = webApplication.ContentDatabases;  2. 如果一定要构造SPWeb和SPSite对象,务必注意使用完后对象的释放,可以使用以下三种技术:
  Dispose方法
  using
  try, catch, finally
  使用using如下:








view sourceprint?







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.







view sourceprint?







01
String str;





02
SPSite oSPSite = null;





03
SPWeb oSPWeb = null;





04






05
try





06
{





07
   oSPSite = new SPSite("http://server/");





08
   oSPWeb = oSPSite.OpenWeb(..);





09






10
   str = oSPWeb.Title;





11
}





12
catch(Exception e)





13
{





14
   //Handle exception, log exception, etc.





15
}





16
finally





17
{





18
   if (oSPWeb != null)





19
   oSPWeb.Dispose();





20






21
   if (oSPSite != null)





22
      oSPSite.Dispose();





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








view sourceprint?







01
String str;





02
SPSite oSPSite = null;





03
SPWeb oSPWeb = null;





04






05
try





06
{





07
   oSPSite = new SPSite("http://server/");





08
   oSPWeb = oSPSite.OpenWeb(..);





09






10
   str = oSPWeb.Title;





11
   if(bDoRedirection)





12
   {





13
       if (oSPWeb != null)





14
          oSPWeb.Dispose();





15
      





16
       if (oSPSite != null)





17
          oSPSite.Dispose();





18






19
       Response.Redirect("newpage.aspx");





20
   }





21
}





22
catch(Exception e)





23
{





24
}





25
finally





26
{





27
   if (oSPWeb != null)





28
   oSPWeb.Dispose();





29






30
   if (oSPSite != null)





31
      oSPSite.Dispose();





32
}  以上问题也适用于using。
  不要创建静态的SPWeb和SPSite对象
  调用SPSiteCollection.Add方法创建返回的SPSite对象需要释放;
  通过SPSiteColleciton的索引器SPSiteColleciton[]返回的SPSite对象需要释放;
  在SPSiteCollection集合中进行foreach是的SPSite对象需要释放;
  以下是推荐的做法:








view sourceprint?







01
void SPSiteCollectionForEachNoLeak()





02
{





03
    using (SPSite siteCollectionOuter = new SPSite("http://moss/"))





04
    {





05
      SPWebApplication webApp = siteCollectionOuter.WebApplication;





06
      SPSiteCollection siteCollections = webApp.Sites;





07






08
      foreach (SPSite siteCollectionInner in siteCollections)





09
      {





10
            try





11
            {





12
                // ...





13
            }





14
            finally





15
            {





16
                if(siteCollectionInner != null)





17
                  siteCollectionInner.Dispose();





18
            }





19
      }





20
    } // SPSite object siteCollectionOuter.Dispose() automatically called.





21
}  通过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对象需要释放;
推荐的开发方式如下:



view sourceprint?

01void PersonalSiteNoLeak()
02{
03    // Open a site collection
04    using (SPSite siteCollection = new SPSite("http://moss/"))
05    {
06      UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(siteCollection));
07      UserProfile profile = profileManager.GetUserProfile("domain\\username");
08      using (SPSite personalSite = profile.PersonalSite)
09      {
10            // ...
11      }
12    }
13}
此处可以通过ProfileLoader省去构造新的SPSite以提高性能







view sourceprint?







1
UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();





2
using (SPSite personalSite = myProfile.PersonalSite)





3
{





4
   // ...





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








view sourceprint?







1
IPersonalPage currentMySitePage = this.Page as IPersonalPage;





2
if (currentMySitePage != null && !currentMySitePage.IsProfileError)





3
{





4
    SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak. // ...





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




view sourceprint?

1void SPControlBADPractice()
2{
3    SPSite siteCollection = SPControl.GetContextSite(Context);
4    //siteCollection.Dispose();   不要释放
5    SPWeb web = SPControl.GetContextWeb(Context);
6    //web.Dispose();不要释放
7}
SPLimitedWebPartManager含有内部对SPWeb的引用,需要释放



view sourceprint?

01void SPLimitedWebPartManagerLeak()
02{
03    using (SPSite siteCollection = new SPSite("http://moss/"))
04    {
05      using (SPWeb web = siteCollection.OpenWeb())
06      {
07            SPFile page = web.GetFile("Source_Folder_Name/Source_Page");
08            SPLimitedWebPartManager webPartManager =
09                page.GetLimitedWebPartManager(PersonalizationScope.Shared);
10                webPartManaber.Web.Dispose();
11      } // SPWeb object web.Dispose() automatically called.
12    }// SPSite object siteCollection.Dispose() automatically called.
13}
Microsoft.SharePoint.Publishing.PublishingWeb(SharePoint2007 only)
  PublishingWeb.GetPublishingWebs会返回PublishingWebCollection,foreach时候需要调用close方法释放每一个对象:








view sourceprint?







01
void PublishingWebCollectionNoLeak()





02
{





03
    using (SPSite siteCollection = new SPSite("http://moss/"))





04
    {





05
      using (SPWeb web = siteCollection.OpenWeb())





06
      {





07
            // Passing in SPWeb object that you own, no dispose needed on





08
            // outerPubWeb.





09
            PublishingWeb outerPubWeb = PublishingWeb.GetPublishingWeb(web);





10
            PublishingWebCollection pubWebCollection = outerPubWeb.GetPublishingWebs();





11
            foreach (PublishingWeb innerPubWeb in pubWebCollection)





12
            {





13
                try





14
                {





15
                  // ...





16
                }





17
                finally





18
                {





19
                  if(innerPubWeb != null)





20
                        innerPubWeb.Close();





21
                }





22
            }





23
      }// SPWeb object web.Dispose() automatically called.





24
    } // SPSite object siteCollection.Dispose() automatically called.





25
}  同样,调用PublishingWebCollection.Add返回的PublishingWeb也需要释放;
  3.在事件处理器里可以使用以下方法避免生成新的SPSite或者SPWeb








view sourceprint?







1
// Retrieve SPWeb and SPListItem from SPItemEventProperties instead of





2
// from a new instance of SPSite.





3
SPWeb web = properties.OpenWeb();//此处通过properties.OpenWeb()返回的SPWeb不用释放;





4
// Operate on the SPWeb object.





5
SPListItem item = properties.ListItem;





6
// 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条,采用分页技术:








view sourceprint?







01
SPQuery query = new SPQuery();





02
SPListItemCollection spListItems ;string lastItemIdOnPage = null; // Page position.





03
int itemCount = 2000   while (itemCount == 2000)





04
{





05
    // Include only the fields you will use.





06
    query.ViewFields = "<FieldRef Name=\"ID\"/><FieldRef Name=\"ContentTypeId\"/>";   query.RowLimit = 2000; // Only select the top 2000.





07
    // Include items in a subfolder (if necessary).





08
    query.ViewAttributes = "Scope=\"Recursive\"";





09
    StringBuilder sb = new StringBuilder();





10
    // To make the query order by ID and stop scanning the table, specify the OrderBy override attribute.





11
    sb.Append("<OrderBy Override=\"TRUE\"><FieldRef Name=\"ID\"/></OrderBy>");





12
    //.. Append more text as necessary ..





13
    query.Query = sb.ToString();   // Get 2,000 more items.   SPListItemCollectionPosition pos = new SPListItemCollectionPosition(lastItemIdOnPage);





14
    query.ListItemCollectionPosition = pos; //Page info.





15
    spListItems = spList.GetItems(query);





16
    lastItemIdOnPage = spListItems.ListItemCollectionPosition.PagingInfo;





17
    // Code to enumerate the spListItems.





18
    // If itemCount <2000, finish the enumeration.





19
    itemCount = spListItems.Count;





20






21
}  分页显示:








view sourceprint?







01
SPWeb oWebsite = SPContext.Current.Web;





02
SPList oList = oWebsite.Lists["Announcements"];





03






04
SPQuery oQuery = new SPQuery();





05
oQuery.RowLimit = 10;





06
int intIndex = 1;





07






08
do





09
{





10
    Response.Write("<BR>Page: " + intIndex + "<BR>");





11
    SPListItemCollection collListItems = oList.GetItems(oQuery);





12






13
    foreach (SPListItem oListItem in collListItems)





14
    {





15
      Response.Write(SPEncode.HtmlEncode(oListItem["Title"].ToString()) +"<BR>");





16
    }





17






18
    oQuery.ListItemCollectionPosition = collListItems.ListItemCollectionPosition;





19
    intIndex++;





20
} while (oQuery.ListItemCollectionPosition != null);



  性能差不推荐使用的API

  性能更好的推荐使用的API


  SPList.Items.Count

  SPList.ItemCount


  SPList.Items.XmlDataSchema

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

  SPList.Items.NumberOfFields

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

  SPList.Items

  SPList.GetItemByUniqueId(System.Guid)


  SPList.Items

  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
页: [1]
查看完整版本: SharePoint 2010开发最佳实践指南