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

最終更新日: 2011年9月22日

適用対象: SharePoint Foundation 2010

この記事の内容
サポートされていないクエリ
2 段階のクエリ
2 段階と 1 段階の違いが問題にならない場合

ここでは、LINQ to SharePoint プロバイダーでサポートされていない、ある種の統合言語クエリ (LINQ) のクエリについて説明します。また、LINQ to SharePoint プロバイダーで Collaborative Application Markup Language (CAML) に変換できない LINQ 演算子を使用するクエリでの 2 段階のプロセスの使用方法についても説明します。

サポートされていないクエリ

C# および Visual Basic の LINQ 構文で利用できるようになったクエリの種類の中には、ある種のデータ ソースの一部のコンテキストでは問題なく動作するのに、Microsoft SharePoint Foundation Web サイト内のデータに対して使用すると許容できないほど低いパフォーマンスを示すものがあります。こうしたクエリは、LINQ to SharePoint プロバイダーではサポートされていません。具体的には、コンテンツ データベースに対する複数の個別のクエリを必要とするクエリがこれに該当します。たとえば、テーブル A に対するクエリが実際にはテーブル A のどの行についてもテーブル B に対する別のクエリを必要とする場合、テーブル A に対するクエリはサポートされません。同様に、以下に示すような LINQ の union キーワードの使用を試みる例は、2 つのリストに対する別々のクエリを必要とするので、サポートされず、例外がスローされます。

DataContext data = new DataContext("http://ContosoServer");            DataContext juniorData = new DataContext("http://ContosoServer/JuniorTeamSite");

EntityList<Announcement> Announcements = data.GetList<Announcement>("Announcements");
EntityList<Announcement> JuniorAnnouncements = juniorData.GetList<Announcement>("Announcements");

var seniorAnns = from ann in Announcements
                 select ann;

var juniorAnns = from ann in JuniorAnnouncements
                 select ann;

IEnumerable<Announcement> mergedAnns = seniorAnns.union(juniorAnns);

foreach (Announcement ann in mergedAnns)
{
    Console.WriteLine(ann.Title);
}

こうしたルールから 1 つわかるのは、2 つの SharePoint Foundation リスト間の暗黙的な結合を前提とするクエリは、結合フィールドがルックアップ フィールドとして構成されている場合に限ってサポートされる、ということです。

2 段階のクエリ

一部の LINQ クエリは、完全には CAML に変換できません。ただし、こうしたクエリであっても、2 段階に分けて実行できるので、原理上は正常に動作します。最初に、LINQ to SharePoint プロバイダーが可能な範囲でクエリを CAML に変換し、そのクエリを実行します。これによって、コンテンツ データベースからフロントエンド Web サーバーに IEnumerable<T> オブジェクトとして渡されるデータのスーパーセットが生成されます。次に、クエリの残り部分が IEnumerable<T> オブジェクトに対する LINQ to Objects クエリとして再構成されます (ただし、最初の段階がコンテンツ データベースに対する単一のクエリまたはクエリ全体によって実行できない場合は例外がスローされます)。

1 段階のクエリの例として、LINQ の select 句 ("select new { c.Name, c.ZipCode }" など) を考えます。これは、2 つの <FieldRef (英語) 子要素を持つ <ViewFields (英語)> タグとして CAML に変換できます。しかし、"select new { c.Price*2, c.Orders, c.Customer }" 句には、CAML でサポートされていない数学関数が含まれています。そのため、このクエリは、LINQ to SharePoint によって変換されてから実行されますが、それも select 句の内容次第です。次に、CAML クエリから受け取った結果が IEnumerable<T> オブジェクトとしてフロントエンド Web サーバーに送信されます。続いて、フロントエンド Web サーバー上の新しい LINQ クエリによって、このオブジェクトに対する select 句のプロジェクションが実行されます。その際には LINQ to Objects の Enumerable.Select() メソッドが使用されます。

サポートされていない LINQ 演算子や 2 段階の実行を必要とする LINQ 演算子を簡単に特定する方法はありません。たとえば、前述の Union() 演算子は、マージ対象のデータ ソースがどちらもコンテンツ データベースから提供されるリストである場合、サポートされません。しかし、それらの一方だけが SharePoint Foundation リストであって、他方がたとえば既にメモリ内にあるテーブルである場合は、Union() を使用できます。

数学的な演算を必要とするクエリ メソッドのほとんどは 2 段階の実行を必要としますが、LINQ to SharePoint が SharePoint Foundation オブジェクト モデルから値を取得できる場合は例外です。たとえば、Count() メソッドや LongCount() メソッドでは、クエリを 2 つの段階に分割する必要がありません。

以下の演算子は、通常、2 段階の実行を必要とし、最初の段階でコンテンツ データベースに対するクエリが 1 つしか必要ない場合であれば正常に動作します。

2 段階と 1 段階の違いが問題にならない場合

クエリのうち CAML 変換が可能な部分の出力を、ToList<TSource>(IEnumerable<TSource>)ToDictionary()、または ToArray<TSource>(IEnumerable<TSource>) の各メソッドによって、IEnumerable<T> オブジェクトに書き込む場合、これらのメソッドの 1 つの呼び出し後に処理されるクエリの部分では、Queryable クラスではなく、Enumerable クラスのクエリ演算子が使用されます。この場合は、2 段階のクエリと 1 段階のクエリとの違いによる影響がありません。

たとえば、以下の 2 つのクエリを比較してみます。どちらも、算術演算子 "-" を使用しているので、2 段階の実行が必要です。しかし、後者のクエリでは、"orders" リスト全体を IList<T> に読み込む from 行でのみ LINQ to SharePoint が使用されているので、このことが明らかになっています。

var ordersInArrears = from order in orders
                      where order.Price – order.Paid > 0
                      select order;

var ordersInArrears = from order in orders.ToList()
                      where order.Price – order.Paid > 0
                      select order;

クエリを LINQ to SharePoint 部分と LINQ to Objects 部分に手動で分割することによっても、それ以外の場合にはサポートされない LINQ キーワードを使用できます。たとえば、以下のような形でお知らせの 2 つのセットをマージする試みは、LINQ to SharePoint クエリが 2 つの IEnumerable<T> オブジェクトに別々に列挙されているので、うまくいきます。使用されている union キーワードは、LINQ to Objects バージョンの Union() です。

DataContext data = new DataContext("http://ContosoServer");            DataContext juniorData = new DataContext("http://ContosoServer/JuniorTeamSite");

EntityList<Announcement> Announcements = data.GetList<Announcement>("Announcements");
EntityList<Announcement> JuniorAnnouncements = juniorData.GetList<Announcement>("Announcements");
List<Announcement> seniorAnns = (from ann in Announcement
                                 select ann).ToList();

List<Announcement> juniorAnns = (from ann in JuniorAnnouncements
                                 select ann).ToList();

IEnumerable<Announcement> mergedAnns = seniorAnns.union(juniorAnns);

foreach (Announcement ann in mergedAnns)
{
    Console.WriteLine(ann.Title);
}

関連項目

参照

IEnumerable<T>

Select

その他の技術情報

LINQ to Objects