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

1SPWebApplication webApplication = SPWebApplication.Lookup(new Uri("http://localhost/");
2SPFarm farm = webApplication.Farm;
3SPContentDatabase 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.
01String str;
02SPSite oSPSite = null;
03SPWeb oSPWeb = null;
04  
05try
06{
07   oSPSite = new SPSite("http://server/");
08   oSPWeb = oSPSite.OpenWeb(..);
09  
10   str = oSPWeb.Title;
11}
12catch(Exception e)
13{
14   //Handle exception, log exception, etc.
15}
16finally
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对象的释放

01String str;
02SPSite oSPSite = null;
03SPWeb oSPWeb = null;
04  
05try
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}
22catch(Exception e)
23{
24}
25finally
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对象需要释放;

以下是推荐的做法:

01void 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对象需要释放;

推荐的开发方式如下:
 
  
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以提高性能
1UserProfile myProfile = ProfileLoader.GetProfileLoader().GetUserProfile();
2using (SPSite personalSite = myProfile.PersonalSite)
3{
4     // ...
5}

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

1IPersonalPage currentMySitePage = this.Page as IPersonalPage; 
2if (currentMySitePage != null && !currentMySitePage.IsProfileError) 
3
4    SPSite personalSite = currentMySitePage.PersonalSite; // Will not leak. // ... 
5}

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

 
  
1void SPControlBADPractice()
2{
3    SPSite siteCollection = SPControl.GetContextSite(Context);
4    //siteCollection.Dispose();   不要释放
5    SPWeb web = SPControl.GetContextWeb(Context);
6    //web.Dispose();  不要释放
7}
SPLimitedWebPartManager含有内部对SPWeb的引用,需要释放
 
  
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方法释放每一个对象:

01void 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

1// Retrieve SPWeb and SPListItem from SPItemEventProperties instead of 
2// from a new instance of SPSite. 
3SPWeb web = properties.OpenWeb();//此处通过properties.OpenWeb()返回的SPWeb不用释放; 
4// Operate on the SPWeb object. 
5SPListItem 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条,采用分页技术:

01SPQuery query = new SPQuery();
02SPListItemCollection spListItems ;  string lastItemIdOnPage = null; // Page position.
03int 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}

分页显示:

01SPWeb oWebsite = SPContext.Current.Web;
02SPList oList = oWebsite.Lists["Announcements"];
03  
04SPQuery oQuery = new SPQuery();
05oQuery.RowLimit = 10;
06int intIndex = 1;
07  
08do
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[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

转载于:https://www.cnblogs.com/Little-Li/archive/2011/05/24/2055428.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值