在 SharePoint Server 中编写有效代码

上次修改时间: 2009年11月30日

适用范围: SharePoint Server 2010

如果使用 SharePoint 对象模型编写自定义解决方案,应该注意与性能、可扩展性和可伸缩性相关的常见问题。本主题可以帮助您有效地对现有 SharePoint 应用程序进行故障排除并提高其性能,或者编写新 SharePoint 应用程序。在这两种情况下,都需要了解如何使 SharePoint 对象模型高效地工作,以及如何将常规编程技巧专门应用于 SharePoint 平台。

使用 SPQuery 对象

设计合理的查询可以帮助您避免随着时间的推移而可能出现的性能问题,因为网站的列表和文件夹数目会不断增加。以下技巧可以帮助您尽可能最高效地使用 SPQuery 对象。

  • 使用界定的 SPQuery 对象。

    RowLimit 没有相应值的 SPQuery 性能不佳,并且无法用于大型列表。请为 RowLimit 指定一个 1 到 2000 之间的值,并根据需要分页显示列表。要获取演示如何执行此操作的代码示例,请参阅处理大型文件夹和列表

  • 限制结果集。

    如果查询返回的项目多于在 Microsoft SharePoint Foundation 2010 中配置的查询阈值,则将阻止查询并且不显示结果。默认查询阈值为 5,000 项,但您可以将其设置为更小的值。对于 Microsoft SharePoint Server 2010 查询,如果您需要所有项目,请使用 ContentIterator.MaxItemsPerQuery 并分页显示结果。

  • 使用索引字段。

    如果所查询的字段未编制索引,并且生成的扫描在列表中遇到的项目多于查询阈值,则将阻止查询。请将 SPQuery.RowLimit 设置为小于查询阈值的值。确保 ContentIterator.MaxItemsPerQuery 的值小于或等于该阈值,这是为 SharePoint Server 2010 推荐的值。

  • 确保包含以下三个 OrderBy 子句之一,以便允许使用索引:ContentIterator.ItemEnumerationOrderByID、ContentIterator.ItemEnumerationOrderByPath 或 ContentIterator.ItemEnumerationOrderByNVPField。

    如果没有 OrderBy 子句,则查询可能会被阻止。SharePoint Server 2010 添加了默认 OrderBy 子句,该子句按内容类型进行排序,以便确保先返回文件夹,然后再返回列表项。除非使用上面列出的三个 OrderBy 子句之一覆盖此行为,否则查询将不能充分发挥使用索引字段的优势,并且当查询的限制不够严格,无法保证返回的项目少于最大项目数时,查询将会被阻止。以下代码示例演示如何使用 ContentIterator.ItemEnumerationOrderByNVPField 子句。该示例假定您要查询索引字段。

    SPQuery query = new SPQuery();
    query.Query = 
    "<Where><Eq><FieldRef Name=\"MyIndexedField\"/><Value Type=\"Text\">FieldValue</Value></Eq></Where>" 
    + ContentIterator.ItemEnumerationOrderByNVPField;
    ContentIterator ci = new ContentIterator();
    ci.ProcessItemsInList(query,
        delegate(SPListItem item)
        {
            // Work on each item.
        },
        delegate(SPListItem item, Exception e)
        {
            // Handle an exception that was thrown while iterating.
            // Return true so that ContentIterator rethrows the exception.
            return true;
        }
    );
    
    Dim query As New SPQuery()
    query.Query = "<Where><Eq><FieldRef Name=""MyIndexedField""/><Value Type=""Text"">FieldValue</Value></Eq></Where>" & ContentIterator.ItemEnumerationOrderByNVPField
    Dim ci As New ContentIterator()
    ci.ProcessItemsInList(query,
                                  Function(item As SPListItem)
                                      'Work on each item.
                                  End Function,
                                  Function(item As SPListItem, e As Exception)
                                      'Handle an exception that was thrown while iterating.
                                      'Return true so that ContentIterator rethrows the exception.
                                      Return True
                                  End Function)
    

使用 PortalSiteMapProvider

使用 PortalSiteMapProvider(仅限 Microsoft Office SharePoint Server 2007 和 Microsoft SharePoint Server 2010)。

Steve Peschka 的白皮书在 Office SharePoint Server 2007 中处理大型列表(该链接可能指向英文页面)介绍了使用 PortalSiteMapProvider 类检索列表数据的有效方法。PortalSiteMapProvider 提供了用于检索列表数据的自动缓存基础结构。PortalSiteMapProvider 的 GetCachedListItemsByQuery 方法采用 SPQuery 对象作为参数,然后检查其缓存以确定项目是否已经存在。如果已经存在,则该方法返回缓存的结果。如果不存在,则该方法查询列表并将结果存储在缓存中。当检索的列表数据在一段时间内不会频繁更改时,该方法尤其有效。如果数据集频繁更改,则除了从数据库读取数据所产生的开销外,该类还会因为不断写入缓存而产生性能开销。请注意,PortalSiteMapProvider 类使用网站集对象缓存来存储数据。此缓存默认大小为 100 MB。您可以在每个网站集的对象缓存设置页上增加网站集的此缓存大小。但是,由于此内存来自供应用程序池使用的共享内存,因此可能会影响其他应用程序的性能。另一个重要限制是您不能在基于 Windows 窗体的应用程序中使用 PortalSiteMapProvider 类。以下代码示例显示如何使用此方法。

良好的编码实践

使用 PortalSiteMapProvider

// Get the current SPWeb object. 
SPWeb curWeb = SPControl.GetContextWeb(HttpContext.Current); 

// Create the query. 
SPQuery curQry = new SPQuery(); 
curQry.Query = "<Where><Eq><FieldRef Name='Expense_x0020_Category'/>
<Value Type='Text'>Hotel</Value></Eq></Where>"; 

// Create an instance of PortalSiteMapProvider. 
PortalSiteMapProvider ps = PortalSiteMapProvider.WebSiteMapProvider; 
PortalWebSiteMapNode pNode = ps.FindSiteMapNode(curWeb.ServerRelativeUrl) as PortalWebSiteMapNode; 

// Retrieve the items. 

SiteMapNodeCollection pItems = ps.GetCachedListItemsByQuery(pNode, "myListName_NotID", curQry, curWeb); 

// Enumerate through all of the matches. 
foreach (PortalListItemSiteMapNode pItem in pItems)
   { 
   // Do something with each match.
   }
' Get the current SPWeb object. 
Dim curWeb As SPWeb = SPControl.GetContextWeb(HttpContext.Current)

' Create the query. 
Dim curQry As New SPQuery()
curQry.Query = "<Where><Eq><FieldRef Name='Expense_x0020_Category'/> <Value Type='Text'>Hotel</Value></Eq></Where>"

' Create an instance of PortalSiteMapProvider. 
Dim ps As PortalSiteMapProvider = PortalSiteMapProvider.WebSiteMapProvider
Dim pNode As PortalWebSiteMapNode = TryCast(ps.FindSiteMapNode(curWeb.ServerRelativeUrl), PortalWebSiteMapNode)

' Retrieve the items. 

Dim pItems As SiteMapNodeCollection = ps.GetCachedListItemsByQuery(pNode, "myListName_NotID", curQry, curWeb)

' Enumerate through all of the matches. 
For Each pItem As PortalListItemSiteMapNode In pItems
   ' Do something with each match.
Next pItem