LINQ to SharePoint を使用してクエリを実行する

最終更新日: 2010年10月8日

適用対象: SharePoint Foundation 2010

この記事の内容
手順 1: Web サイトへの参照を取得する
手順 2: リストへの参照を取得する
手順 3 (オプション): オブジェクトの変更履歴をオフにする
手順 4: LINQ クエリを定義する
手順 5: クエリ結果を列挙する
手順 6 (オプション): 複数のリストおよび複数のデータ ソースの結果を結合する
手順 7 (オプション): 複数のデータ ソースの結果を結合する

このトピックでは、LINQ to SharePoint プロバイダーを使用して Microsoft SharePoint Foundation リストにクエリを実行する方法について説明します。

手順 1: Web サイトへの参照を取得する

LINQ to SharePoint プロバイダーに対するコーディングを開始するには、まず、DataContext オブジェクトを作成します。このオブジェクトは、コンテンツ データベースのサブセット、特に SharePoint Foundation Web サイトのリストおよびリスト アイテムを表します。次に示すように、クエリを実行する Web サイトの絶対 URL をリテラル文字列としてクラス コンストラクターに渡すと、こうしたオブジェクトを最も簡単に作成することができます。

DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");

しかし、多くの場合、ソリューションは多くのファームの多くのサイトに対して実行する必要があるため、コードを記述するときに完全な URL がわかりません。Web パーツカスタム アプリケーション ページのような、HTTP コンテキストの任意の状況でコードが実行されている場合、また、現在の Web サイトに対してクエリを実行する場合は、次の例で示すように SPContext オブジェクトを使用して URL を取得できます。

DataContext teamSite = new DataContext(SPContext.Current.Web.Url);

また、コンテキストを使用して、サイト コレクション内の他の Web サイトの URL、また Web アプリケーションの他のサイト コレクションを間接的に取得することもできます。たとえば、次のコードでは、Web アプリケーション内の 1 番古いサイト コレクションの最上位 Web サイトに対して DataContext を作成しています。

String rootOfOldestURL = this.Site.WebApplication.Sites[0].RootWeb.Url;      
using (DataContext topSiteOfOldestSiteCollection = new DataContext(rootOfOldestURL))
{
}

DataContext オブジェクトを廃棄する必要があることに注意してください。このオブジェクトでは、SPContext によって提供されるものとは異なる SPWeb オブジェクトを使用しています。

注意

SPWebApplication オブジェクトへの参照には、名前空間 Microsoft.SharePoint.Administration の using ステートメント (Visual Basic では Imports) が必要です。

SharePoint Foundation ファーム内では他の方法で Web サイトへの参照を取得することもできます。その方法については、「サイト、Web アプリケーション、およびその他の主要オブジェクトへの参照を取得する」を参照してください。

コンソール アプリケーションのように HTTP コンテキストがない場合、また、コーディング時にサーバー名がわからない場合は、次の例で示すように、"localhost" エイリアスを使用して、ルート Web サイトへの参照を取得できます。

using (DataContext topLevelSite = new DataContext("https://localhost"))
{
}

using (DataContext mySite = new DataContext("https://localhost/sites/MySite"))
{
}

HTTP コンテキストがないので、DataContext オブジェクトは破棄する必要があります。

DataContext からクラスを派生させることができます。この場合は、次の例で示すように、クラスのコンストラクターを使用します。

ContosoTeamData teamSite = new ContosoTeamData(SPContext.Current.Web.Url);

手順 2: リストへの参照を取得する

GetList<T>(String) メソッドを使用して、EntityList<TEntity> オブジェクトを取得します。このオブジェクトは、リストの IQueryable<T> 表現です。次に例を示します。

EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")

リストのコンテンツ タイプは明示的に宣言されたクラス (この場合は "Announcement") によって表す必要があることに注意してください。このクラスは、SharePoint Foundation Web サイトのコンテンツ タイプの名前およびコンテンツ タイプの ID を指定する ContentTypeAttribute で修飾されている必要があります。一般的には、コードを記述するときに、そのコードによってどのリストに対してクエリが実行されるかを認識しておく必要があります。少なくとも、コンテンツ タイプを表すクラスには、リスト内の列を表すプロパティが 1 つ以上含まれていなければなりません。また、プロパティ宣言は、少なくともフィールドとその型を指定する ColumnAttribute で修飾する必要があります。次の例は、コードの呼び出しを有効にして、GetList<T>(String) (T は Announcement) メソッドを呼び出すのに必要な最小の宣言を示しています。

[ContentType(Name="Announcement", Id="0x0104")]
public partial class Announcement
{
    [Column(Name = "Title", FieldType = "Text")] 
    public String Title { get; set; }
}

ただし、クエリで参照できるのは、コンテンツ タイプ クラスのプロパティによって表される列だけです。したがって、この最小宣言が提供されている場合は、次の例で示すように、コードの呼び出しは Title フィールド (列) のみを参照できます。

var excitingAnnouncements = from announcement in announcements
                            where announcement.Title.EndsWith("!")
                            select announcement;

注意

この例で示すように、クラスの名前は Announcement ですが、これは同じ名前の SharePoint Foundation コンテンツ タイプを正確にミラーする必要があることを意味するものではありません。つまり、コンテンツ タイプのすべての列に必ずしもプロパティが必要であるとは限りません。クラスは、コンテンツ タイプの列のサブセットのみを表す場合があります。また、追加のメンバーを含めることもできます。実際のところ、これは LINQ to SharePoint プロバイダーの重要な特性です。サイト所有者が列をリストに追加でき、これにより、リストに対して新しいコンテンツ タイプが実際に作成されるからです。また、クラスの名前は、リストのコンテンツ タイプと同じである必要はありません。GetList<T>(String) メソッドへの呼び出しで type パラメーターとして同じ名前が使用されている場合は、どのような名前でも付けることができます。ただし、クラスの名前が、リストの正式なコンテンツ タイプと同じだと、通常は、コードがより読みやすくなります。既定では、SPMetal ツールはこのプラクティスに従っています。

コンテンツ タイプは他のコンテンツ タイプから継承できます。GetList<T>(String) メソッドへの呼び出しについては、継承ツリー内で上位にあるすべてのコンテンツ タイプを type パラメーターとして使用できます。たとえば、次の例で示すように、すべてのコンテンツ タイプが基本の Item コンテンツ タイプから派生しているため、Item を type パラメーターとして使用できます。

EntityList<Item> announcements = teamSite.GetList<Item>("Announcements")

ソース コードは、Item クラスの宣言を提供する必要があります。また、クラスはクエリが参照するコンテンツ タイプの列ごとにプロパティを宣言する必要があります。

Item コンテンツ タイプを使用している場合は、次の例で示すように、コーディングの際に、リストまたは派生するコンテンツ タイプの名前を知らなくてもリストに対してクエリを実行できます。

DataContext topLevelSite = new DataContext("https://localhost");
SPSite siteCollection = new SPSite("https://localhost");
EntityList<Item> someList = topLevelSite.GetList<Item>(siteCollection.RootWeb.Lists[0].Title);

var first3Items = from item in someList
                  where item.Id <= 3
                  select item;

foreach (var item in first3Items)
{
    Console.Writeline("{0} is one of the first 3 items in {1}", item.Title, someList.Title);
}

ただし、実用的な LINQ to SharePoint コーディングの現実的なシナリオには、ほとんどの場合、特定のリストに固有の列が含まれるため、実際には、リスト名とその特定の派生コンテンツ タイプを認識しておく必要があります。特に、コーディングの際は、クエリ コードを実行する先の Web サイト上に特定のリストが存在することを想定できる必要があります。さらに、指定したリストのクエリは特定の列を参照するため、指定したリストに対しては、列の所定のサブセットがリストに存在することを想定できなければなりません。これは、SharePoint Foundation ソリューションが、次の 2 つの種類のどちらかであることを意味します。

  • SharePoint Foundation、または Microsoft SharePoint Server のような高機能な製品に付属する、よく知られているリストの種類にクエリを実行できるように設計される。

  • 機能セットの一部またはカスタム サイト定義として、1 つまたは複数のカスタム リストのセットと共に開発され、インストールされる。

SPMetal ツールを使用して、必要なクラスとプロパティの宣言を生成することをお勧めします。

手順 3 (オプション): オブジェクトの変更履歴をオフにする

コードでリストに対してクエリを実行するだけの場合、つまりリスト アイテムを追加、削除、編集しない場合は、オブジェクトの変更履歴をオフにできます。これによりパフォーマンスが向上します。これを行うには、ObjectTrackingEnabled プロパティを false に設定します。

teamSite.ObjectTrackingEnabled = false;

手順 4: LINQ クエリを定義する

そのクエリ内の LINQ の値は、データ ソースまたは LINQ プロバイダーに関係なく、基本的には同じ方法で書き込まれます。データ コンテキストへの参照の取得方法と IQueryable<T> オブジェクトの取得方法に若干の違いがありますが、LINQ to SharePoint クエリは、LINQ to SQL または LINQ to XML で使用するクエリとほぼ同じです。詳細については、「LINQ to SQL: リレーショナル データのための .NET 統合言語クエリ」および「LINQ to XML (英語)」を参照してください。

ただし、まったく同じ LINQ プロバイダーは 2 つとありません。データ ソースのネイティブなクエリ言語 (プロバイダーによって LINQ クエリがこのクエリ言語に変換されます) の違いによって、異なる制限がクエリの可能性に強制的に適用されることがあります。特に、LINQ to SharePoint プロバイダーを使用するクエリ内には、暗黙的または明示的なリスト結合に制限があります。LINQ to SharePoint クエリは、明示的または暗黙的に 2 つのリストを結合できますが、それには一方のテーブルにもう一方のテーブルの列を検索する Lookup 型列が必要です。Lookup フィールドで 1 つの値しか許可されていない場合、リスト間のこの関係は、リスト内のコンテンツ タイプを表すクラス内の EntityRef<TEntity> フィールドを使用して、コードで表す必要があります。フィールドで複数の値が許可されている場合、関係は、EntitySet<TEntity> フィールドと、そのフィールドをラップする EntitySet<TEntity> プロパティによって表す必要があります。

ヒントヒント

Log プロパティは、CAML クエリを書き込むことができる TextWriter です。LINQ クエリは、この CAML クエリに変換されます。CAML を表示できるとデバッグ時に便利です。これを行うには、TextWriter オブジェクトを Log プロパティに割り当てます。次の例では、OutTextWriterLog に割り当てられています。これにより、次に示すように、CAML クエリは、LINQ クエリがコンソール アプリケーションで実行されるときにコンソールに表示されます。

#if DEBUG
teamSite.Log = Console.Out;
#endif

手順 5: クエリ結果を列挙する

すべての LINQ プロバイダーと同様、LINQ to SharePoint クエリは、列挙されるまで実行されません。これは、通常、foreach ループで発生します。カスタム列挙が必要な特別な状況では、たとえば、結果でアイテムを 1 つおきにスキップする場合は、IEnumerable および IEnumerator のメソッドを使用できます。

クエリの結果を、匿名型 var (Visual Basic では Dim) ではなく、IList<T> オブジェクトや ICollection<T> オブジェクトのような IEnumerable<T> 変数に割り当てることもできます。これにより、変数を設定できるようにクエリを直ちに実行できます。次に例を示します。

EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")

IList<Announcement> excitingAnnouncements = from announcement in announcements
                                            where announcement.Title.EndsWith("!")
                                            select announcement;

手順 6 (オプション): 複数のリストおよび複数のデータ ソースの結果を結合する

複数のリストの結果を 1 つの IList<T> に結合し、LINQ to Objects を使用してさらにフィルター処理できます。次の例は、企業イベントおよびチーム イベント両方の IList<T> を生成する方法、および講堂で行われる現在の日付のイベントのレポートを生成する方法を示しています。

DataContext corpSiteData = new DataContext("https://localhost/CorpSite");
DataContext markTeamData = new DataContext("https://localhost/Marketing");

EntityList<Event> allCorpEvents = corpSiteData.GetList<Event>("Calendar");
EntityList<Event> allMarkTeamEvents = markTeamData.GetList<Event>("Calendar");

List<Event> todaysCorpEvents = (from ev in allCorpEvents
                  where ev.StartDate = DateTime.Now
                  select ev).ToList();

List<Event> todaysTeamEvents = (from ev in allMarkTeamEvents
                  where ev.StartDate = DateTime.Now
                  select ev).ToList();

IEnumerable<Event> mergedEvents = todaysCorpEvents.Union(todaysTeamEvents);

var todaysAuditoriumEventTitles = from ev in mergedEvents
                  where ev.Location.Contains("Auditorium")
                  select new { ev.Title };

foreach (var eventTitle in todaysAuditoriumEventTitles)
{
    Console.WriteLine(eventTitle.Title);
}

2 つの IList<T> オブジェクトには、同じ type パラメーターがあります。

手順 7 (オプション): 複数のデータ ソースの結果を結合する

手順 7. で説明する、複数の SharePoint Foundation リストのデータを結合する手法を使用すると、SharePoint Foundation リストのデータと他のソースのデータを結合できます。ただし、これを行うには、他のソースのレコードが、SharePoint Foundation コンテンツ タイプを表すクラスにキャスト可能である必要があります。たとえば、コードによって、LINQ to SQL を使用して、oldClients という名前の IList<T> (T は Client) オブジェクトを生成するとします。また、LINQ to SharePoint を使用して、activePatients という名前の IList<T> (T は Patient) オブジェクトを生成するとします。Client クラスのプロパティと他のメンバーが Patient クラスのメンバーのサブセットで、対応するメンバーに同じ署名がある場合は、次の例で示すように、SQL ソースのデータを、SharePoint Foundation リストのデータに結合できます。

foreach (Patient patient in activePatients)
{
    oldClients.Add((Client)patient);
}

2 つの LINQ プロバイダーのデータを結合する必要がある場合は、両方のプロバイダーで同じアイテムの種類のクラスを使用することをお勧めします。これが可能なのは、LINQ to SharePoint に必要な属性装飾と、他の LINQ プロバイダーに必要な属性装飾を、同じクラス宣言に配置できるからです。次の例は、LINQ to SharePoint の ContentTypeAttribute と LINQ to SQL の TableAttribute によって装飾されているクラス宣言の署名を示しています。

[ContentType(Name="Item", Id="0x01" List="Customers")]
[Table(Name = "Customers")]
public partial class Customer : Item
{
}

これらの属性を設定すると、SQL テーブルのデータと SharePoint Foundation リストのデータを結合するために種類をキャストする必要はありません。

関連項目

参照

SPMetal

ColumnAttribute

ContentTypeAttribute

DataContext

EntityList<TEntity>

EntityRef<TEntity>

EntitySet<TEntity>

ICollection<T>

IEnumerable

IEnumerator

IList<T>

IQueryable<T>

SPContext

SPWebApplication

TableAttribute

概念

コンテンツ タイプ ID

サポートされていない LINQ クエリと 2 段階のクエリ

サイト、Web アプリケーション、およびその他の主要オブジェクトへの参照を取得する

[方法] LINQ to SharePoint を使用してコンテンツ データベースに書き込む

その他の技術情報

LINQ to Objects

LINQ to SQL: リレーショナル データのための .NET 統合言語クエリ

LINQ to XML (英語)

Web Parts Overview