LINQ に関する留意点 (WCF Data Services)
このトピックでは、WCF Data Services クライアントを使用しているときに LINQ クエリを作成および実行する方法と、Open Data Protocol (OData) を実装するデータ サービスを LINQ で照会する場合の制限について説明します。 詳細情報: OData ベースのデータ サービスに対するクエリの作成および実行については、「データ サービスのクエリ (WCF Data Services)」を参照してください。
このトピックは、次のセクションで構成されています。
LINQ クエリの作成
LINQ を使用すると、IEnumerable<T> を実装するオブジェクトのコレクションに対するクエリを作成できます。 Visual Studio の [サービス参照の追加] ダイアログ ボックスと DataSvcUtil.exe ツールでは、DataServiceContext を継承するエンティティ コンテナー クラスとしての OData サービスの表現や、フィードで返されるエンティティを表すオブジェクトを生成できます。 これらのツールでは、サービスによってフィードとして公開されるコレクションに対応するエンティティ コンテナー クラスのプロパティも生成されます。 データ サービスをカプセル化するクラスのこれらのプロパティは、それぞれ DataServiceQuery<TElement> を返します。 DataServiceQuery<TElement> クラスは LINQ で定義された IQueryable<T> インターフェイスを実装するので、データ サービスによって公開されるフィードに対する LINQ クエリを作成できます。作成した LINQ クエリは、クライアント ライブラリにより、実行時にデータ サービスに送信されるクエリ要求 URI に変換されます。
重要
LINQ 構文で表現できるクエリのセットは、OData データ サービスによって使用される URI 構文で有効なクエリのセットよりも範囲が広くなります。クエリを対象データ サービスの URI にマップできない場合、NotSupportedException が発生します。詳細については、次のトピックを参照してください。このトピックの「Unsupported LINQ Methods」を参照してください。
次の例の LINQ クエリは、輸送費が 30 ドルを超える Orders を取得し、結果を出荷日の新しい順に並べ替えます。
Dim selectedOrders = From o In context.Orders _
Where (o.Freight > 30) _
Order By o.ShippedDate Descending _
Select o
var selectedOrders = from o in context.Orders
where o.Freight > 30
orderby o.ShippedDate descending
select o;
この LINQ クエリは、Northwind ベースのクイック スタート データ サービスに対して実行される次のクエリ URI に変換されます。
https://localhost:12345/Northwind.svc/Orders?Orderby=ShippedDate&?filter=Freight gt 30
LINQ 全般の詳細については、「Language-Integrated Query (LINQ)」を参照してください。
LINQ を使用してクエリを作成する際には、前の例のような言語固有の宣言型のクエリ構文と、標準クエリ演算子と呼ばれる一連のクエリ メソッドの両方を使用できます。 したがって、次の例のように、前の例と同等のクエリをメソッド ベースの構文のみを使用して作成することもできます。
Dim selectedOrders = context.Orders _
.Where(Function(o) o.Freight.Value > 30) _
.OrderByDescending(Function(o) o.ShippedDate)
var selectedOrders = context.Orders
.Where(o => o.Freight > 30)
.OrderByDescending(o => o.ShippedDate);
どちらの方法で作成したクエリも、WCF Data Services クライアントによってクエリ URI に変換されます。クエリ式にクエリ メソッドを追加して LINQ クエリを拡張することもできます。 クエリ式または DataServiceQuery<TElement> にメソッド構文を追加して LINQ クエリを作成した場合は、メソッドが呼び出される順序で操作がクエリ URI に追加されます。 これは、AddQueryOption(String, Object) メソッドを呼び出して各クエリ オプションをクエリ URI に追加するのと同じです。
LINQ クエリの実行
クエリに特定の LINQ クエリ メソッド (First<TSource>、Single<TSource> など) を追加するとクエリが実行されます。 クエリは、結果が暗黙的に列挙される場合 (foreach ループの間など) や、クエリが List コレクションに割り当てられている場合にも実行されます。 詳細については、「データ サービスのクエリ (WCF Data Services)」を参照してください。
クライアントによる LINQ クエリの実行は 2 つの部分に分かれています。 可能な限り、クエリ内の LINQ 式は最初にクライアントで評価されます。その後、URI ベースのクエリが生成され、データ サービスに送信されて、サービス内のデータに対して評価されます。 詳細については、「データ サービスのクエリ (WCF Data Services)」の「Client versus Server Execution」を参照してください。
OData 準拠のクエリ URI で LINQ クエリを変換できない場合は、クエリを実行しようとすると例外が発生します。 詳細については、「データ サービスのクエリ (WCF Data Services)」を参照してください。
LINQ クエリの例
以下のセクションの各例は、OData サービスに対して実行できる LINQ クエリの種類を示しています。
フィルター処理
このセクションの LINQ クエリは、サービスによって返されるフィード内のデータをフィルター処理します。
以下の各例は、返された Orders エンティティをフィルター処理して、輸送費が 30 ドルを超える注文のみが返されるようにする同等のクエリを示しています。
LINQ クエリ構文を使用する場合:
Dim filteredOrders = From o In context.Orders Where o.Freight.Value > 30 Select o
var filteredOrders = from o in context.Orders where o.Freight > 30 select o;
LINQ クエリ メソッドを使用する場合:
Dim filteredOrders = context.Orders.Where(Function(o) o.Freight.Value > 0)
var filteredOrders = context.Orders .Where(o => o.Freight > 30);
URI クエリ文字列オプション $filter:
' Define a query for orders with a Freight value greater than 30. Dim filteredOrders _ = context.Orders.AddQueryOption("$filter", "Freight gt 30M")
// Define a query for orders with a Freight value greater than 30. var filteredOrders = context.Orders.AddQueryOption("$filter", "Freight gt 30M");
上の例はすべて、https://localhost:12345/northwind.svc/Orders()?$filter=Freight gt 30M というクエリ URI に変換されます。
All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) および Any<TSource>(IEnumerable<TSource>) 演算子を使用し、コレクション プロパティに基づいてくエンティティをフィルター処理するクエリを作成することもできます。 この場合、述語は関連エンティティのコレクションとプリミティブ型および複合型のコレクションの両方を返すプロパティに対して評価されます。 たとえば、次のクエリでは指定された文字列を含む説明付きの領域を持つ従業員を返します。
Dim filteredEmployees = From e In context.Employees _
Where e.Territories.Any(Function(t) t.TerritoryDescription.Contains(territory))
Select e
var filteredEmployees = from e in context.Employees
where e.Territories.Any(t => t.TerritoryDescription.Contains(territory))
select e;
このクエリでは、Any<TSource>(IEnumerable<TSource>) 演算子を使用して Employees と Territories 間で多対多のアソシエーションを走査し、関連領域の評価に基づいて従業員をフィルター処理できます。 詳細については、投稿の「WCF Data Services での Any/All のサポート」を参照してください。
並べ替え
以下の各例は、返されたデータを会社名と郵便番号の降順で並べ替える同等のクエリを示しています。
LINQ クエリ構文を使用する場合:
Dim sortedCustomers = From c In context.Customers Order By c.CompanyName Ascending, c.PostalCode Descending Select c
var sortedCustomers = from c in context.Customers orderby c.CompanyName ascending, c.PostalCode descending select c;
LINQ クエリ メソッドを使用する場合:
Dim sortedCustomers = context.Customers.OrderBy(Function(c) c.CompanyName) _ .ThenByDescending(Function(c) c.PostalCode)
var sortedCustomers = context.Customers.OrderBy(c => c.CompanyName) .ThenByDescending(c => c.PostalCode);
URI クエリ文字列オプション $orderby:
Dim sortedCustomers = context.Customers _ .AddQueryOption("$orderby", "CompanyName, PostalCode desc")
var sortedCustomers = context.Customers .AddQueryOption("$orderby", "CompanyName, PostalCode desc");
上の例はすべて、https://localhost:12345/northwind.svc/Customers()?$orderby=CompanyName,PostalCode desc というクエリ URI に変換されます。
射影
以下の各例は、返されたデータをより範囲の狭い CustomerAddress 型に射影する同等のクエリを示しています。
LINQ クエリ構文を使用する場合:
Dim projectedQuery = From c In context.Customers Select New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country }
var projectedQuery = from c in context.Customers select new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country };
LINQ クエリ メソッドを使用する場合:
Dim projectedQuery = context.Customers.Where(Function(c) c.Country = "Germany") _ .Select(Function(c) New CustomerAddress With { .CustomerID = c.CustomerID, .Address = c.Address, .City = c.City, .Region = c.Region, .PostalCode = c.PostalCode, .Country = c.Country })
var projectedQuery = context.Customers.Where(c => c.Country == "Germany") .Select(c => new CustomerAddress { CustomerID = c.CustomerID, Address = c.Address, City = c.City, Region = c.Region, PostalCode = c.PostalCode, Country = c.Country});
注意
AddQueryOption(String, Object) メソッドを使用してクエリ URI に $select クエリ オプションを追加することはできません。LINQ の Select<TSource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>) メソッドを使用して、クライアントによって要求 URI に $select クエリ オプションが生成されるようにすることをお勧めします。
上の例はいずれも、"https://localhost:12345/northwind.svc/Customers()?$filter=Country eq 'GerGerm'&$select=CustomerID,Address,City,Region,PostalCode,Country" というクエリ URI に変換されます。
クライアントのページング
以下の各例は、25 件の注文を含む並べ替え済みの注文エンティティのページを、最初の 50 件の注文をスキップして要求する同等のクエリを示しています。
LINQ クエリにクエリ メソッドを適用する場合:
Dim pagedOrders = (From o In context.Orders Order By o.OrderDate Descending Select o) _ .Skip(50).Take(25)
var pagedOrders = (from o in context.Orders orderby o.OrderDate descending select o).Skip(50).Take(25);
URI クエリ文字列オプション $skip および $top:
Dim pagedOrders = context.Orders _ .AddQueryOption("$orderby", "OrderDate desc") _ .AddQueryOption("$skip", 50) _ .AddQueryOption("$top", 25) _
var pagedOrders = context.Orders .AddQueryOption("$orderby", "OrderDate desc") .AddQueryOption("$skip", 50) .AddQueryOption("$top", 25);
上の例はいずれも、https://localhost:12345/northwind.svc/Orders()?$orderby=OrderDate desc&$skip=50&$top=25 というクエリ URI に変換されます。
展開
OData データ サービスを照会するときに、返されるフィードにクエリの対象のエンティティに関連するエンティティを含めるように要求することができます。 そのためには、LINQ クエリの対象のエンティティ セットの DataServiceQuery<TElement> で Expand(String) メソッドを呼び出して、関連するエンティティ セットの名前を path パラメーターとして渡します。 詳細については、「遅延コンテンツの読み込み (WCF Data Services)」を参照してください。
以下の各例は、クエリで Expand(String) メソッドを使用する同等の方法を示しています。
LINQ クエリ構文の場合:
Dim ordersQuery = From o In context.Orders.Expand("Order_Details") Where o.CustomerID = "ALFKI" Select o
var ordersQuery = from o in context.Orders.Expand("Order_Details") where o.CustomerID == "ALFKI" select o;
LINQ クエリ メソッドの場合:
Dim ordersQuery = context.Orders.Expand("Order_Details") _ .Where(Function(o) o.CustomerID = "ALFKI")
var ordersQuery = context.Orders.Expand("Order_Details") .Where(o => o.CustomerID == "ALFKI");
上の例はいずれも、https://localhost:12345/northwind.svc/Orders()?$filter=CustomerID eq 'ALFKI'&$expand=Order_Details というクエリ URI に変換されます。
サポートされていない LINQ メソッド
次の表に含まれている LINQ メソッドはサポートされていません。OData サービスに対して実行されるクエリにこれらのメソッドを含めることはできません。
演算の種類 |
サポートされていないメソッド |
---|---|
集合演算子 |
以下に示す集合演算子は DataServiceQuery<TElement> に対してサポートされていません。 |
順序付け演算子 |
IComparer<T> を必要とする以下の順序付け演算子は DataServiceQuery<TElement> に対してサポートされていません。 |
プロジェクション演算子とフィルター演算子 |
位置指定引数を受け取る以下のプロジェクション演算子とフィルター演算子は DataServiceQuery<TElement> に対してサポートされていません。 |
グループ化演算子 |
すべてのグループ化演算子は DataServiceQuery<TElement> に対してサポートされていません。以下に例を示します。 グループ化の操作はクライアント側で実行する必要があります。 |
集計演算子 |
すべての集計演算子は DataServiceQuery<TElement> に対してサポートされていません。以下に例を示します。 集計操作は、クライアント側で実行するか、サービス操作でカプセル化する必要があります。 |
ページング演算子 |
以下のページング演算子は DataServiceQuery<TElement> に対してサポートされていません。 注意 空シーケンスで実行されるページング演算子は null を返します。 |
その他の演算子 |
以下に示す演算子は DataServiceQuery<TElement> に対してサポートされていません。 |
サポートされている式の関数
共通言語ランタイム (CLR) の以下のメソッドおよびプロパティは、クエリ式で変換して OData サービスへの要求 URI に含めることができるため、サポートされています。
String メンバー |
サポートされている OData 関数 |
---|---|
string concat(string p0, string p1) |
|
bool substringof(string p0, string p1) |
|
bool endswith(string p0, string p1) |
|
int indexof(string p0, string p1) |
|
int length(string p0) |
|
string replace(string p0, string find, string replace) |
|
string substring(string p0, int pos) |
|
string substring(string p0, int pos, int length) |
|
string tolower(string p0) |
|
string toupper(string p0) |
|
string trim(string p0) |
DateTime メンバー1 |
サポートされている OData 関数 |
---|---|
int day(DateTime p0) |
|
int hour(DateTime p0) |
|
int minute(DateTime p0) |
|
int month(DateTime p0) |
|
int second(DateTime p0) |
|
int year(DateTime p0) |
1Microsoft.VisualBasic.DateAndTime の同等の日時プロパティと Visual Basic の DatePart メソッドもサポートされています。
Math メンバー |
サポートされている OData 関数 |
---|---|
decimal ceiling(decimal p0) |
|
double ceiling(double p0) |
|
decimal floor(decimal p0) |
|
double floor(double p0) |
|
decimal round(decimal p0) |
|
double round(double p0) |
Expression メンバー |
サポートされている OData 関数 |
---|---|
bool isof(type p0) |
クライアント側でその他の CLR 関数を評価できる場合もあります。 クライアント側で評価することも、サーバー側で評価するために有効な要求 URI に変換することもできない式に対しては、NotSupportedException が発生します。
バージョン管理の要件
LINQ サポートには、次に示す OData プロトコルのバージョン管理の要件があります。
- All<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>) および Any<TSource>(IEnumerable<TSource>) 演算子をサポートする場合は、クライアントとデータ サービスの両方でバージョン 3.0 以降の OData プロトコルがサポートされている必要があります。
詳細については、「データ サービスのバージョン管理 (WCF Data Services)」を参照してください。
関連項目
概念
データ サービスのクエリ (WCF Data Services)
オブジェクトの具体化 (WCF Data Services)