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 の 3 つの句 ContentIterator.ItemEnumerationOrderByID、ContentIterator.ItemEnumerationOrderByPath、または ContentIterator.ItemEnumerationOrderByNVPField のいずれかを使用するようにします。

    OrderBy 句がないと、クエリがブロックされる可能性があります。SharePoint Server 2010 が追加する既定の OrderBy 句ではコンテンツ タイプの順になり、リスト アイテムより前にフォルダーが返されます。前記の 3 つの 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 です。このキャッシュのサイズは、サイト コレクションのオブジェクト キャッシュ設定ページでサイト コレクションごとに増やすことができます。ただし、このメモリはアプリケーション プールで使用可能な共有メモリから取得されるので、他のアプリケーションのパフォーマンスが影響を受ける可能性があります。もう 1 つの重要な制限は、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