クエリ結果の構造化 (Entity Framework)

クエリを実行すると、クエリで要求されたオブジェクトのみが返されます。たとえば、Adventure Works Sales Model に対するクエリによって Customer オブジェクトが返される場合、CustomerSalesOrderHeader の間にリレーションシップが存在しても、既定では SalesOrderHeader オブジェクトは返されません。この動作により、アプリケーションはオブジェクト クエリから返されるデータの範囲を常に把握するようになります。既定では、エンティティ型の間のアソシエーションを表すリレーションシップ オブジェクトが常に返されます。オブジェクトが EDM の概念スキーマに基づいて生成されると、ナビゲーション プロパティはアソシエーションの両端のエンティティ オブジェクトに対して生成されます。これらのナビゲーション プロパティは、一対一または多対一のリレーションシップの "一" の側では EntityReference、一対多または多対多のリレーションシップの "多" の側では EntityCollection を返します。詳細については、「Entity Data Model のリレーションシップ」を参照してください。

ナビゲーション プロパティを使用すると、これらのリレーションシップを明示的にナビゲートする Entity SQL または LINQ to Entities クエリを作成できます。詳細については、「ナビゲーション プロパティを使用してリレーションシップをナビゲートする方法 (Entity Framework)」を参照してください。ただし、クエリ結果を構築するために、クエリ内でリレーションシップを明示的にナビゲートする必要はありません。参照オブジェクトも読み込むようにクエリ結果を拡張できる別の方法が 2 つあります。1 つはクエリ パスを指定する方法で、もう 1 つはナビゲーション プロパティを使用して関連オブジェクトを明示的に読み込む方法です。結果をさらに制御するには、クエリ パスを定義してから、選択した関連オブジェクトのみを明示的に読み込みます。

使用するオプションを選択する際には、データベースに対する要求数と 1 つのクエリで返されるデータ量の間でのトレードオフに注意してください。クエリ パスは、クエリによって返されるオブジェクトのグラフを定義します。クエリ パスを定義する場合、1 つの結果セット内のパスによって定義されたすべてのオブジェクトを返すには、データベースに対する 1 つの要求のみが必要になります。オブジェクトを明示的に読み込む場合は、データベースへの複数のラウンドトリップが必要になり、複数の有効な結果セットも必要になることがありますが、返されるデータ量は読み込み中のオブジェクトのみに制限されています。

クエリ結果を構造化するためのクエリ パスの定義

クエリ パスを指定するには、オブジェクト グラフの文字列表記を ObjectQueryInclude メソッドに渡します。このパスは、オブジェクト クエリの実行時に返す関連オブジェクトを指定します。たとえば、Contact オブジェクトに対するクエリで定義されたクエリ パスにより、関連する各 SalesOrderHeader および SalesOrderDetail が必ず返されます。以下の LINQ to Entities、Entity SQL、およびクエリ ビルダ メソッドを使用するクエリの例を参照してください。

  • LINQ to Entities

    ' Define a LINQ query with a path that returns 
    ' orders and items for a contact.
    Dim contacts = (From contact In context.Contact _
        .Include("SalesOrderHeader.SalesOrderDetail") _
        Select contact).FirstOrDefault()
    
    // Define a LINQ query with a path that returns 
    // orders and items for a contact.
    var contacts = (from contact in context.Contact
                  .Include("SalesOrderHeader.SalesOrderDetail")
                  select contact).FirstOrDefault();
    
  • Entity SQL

    ' Define an object query with a path that returns 
    ' orders and items for a specific contact.              
    Dim queryString As String = _
        "SELECT VALUE TOP(1) Contact FROM " + _
        "AdventureWorksEntities.Contact AS Contact"
    
    ' Define the object query with the query string.
    Dim contactQuery As New ObjectQuery(Of Contact)(queryString, _
        context, MergeOption.NoTracking)
    
    Dim contact As Contact = _
    contactQuery.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.              
    string queryString =
        @"SELECT VALUE TOP(1) Contact FROM " + 
        "AdventureWorksEntities.Contact AS Contact";
    
    // Define the object query with the query string.
    ObjectQuery<Contact> contactQuery = new ObjectQuery<Contact>(queryString, 
        context, MergeOption.NoTracking);
    
    Contact contact =
        contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();
    
  • クエリ ビルダ メソッド

    ' Create an object query with a path that returns orders and items for a contact.
    Dim contact As Contact = _
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.
    Contact contact =
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();
    

クエリ パスを定義する際は、次の点に注意してください。

  • クエリ パスは、クエリ ビルダ メソッドおよび LINQ クエリで使用できます。

  • Include を呼び出した場合、クエリ パスは ObjectQuery の返されたインスタンスでのみ有効です。ObjectQuery の他のインスタンスとオブジェクト コンテキスト自体は影響を受けません。

  • Include はクエリ オブジェクトを返すので、このメソッドを ObjectQuery で複数回呼び出すことで、次の例のように複数のリレーションシップのオブジェクトを含めることができます。

    ' Create a SalesOrderHeader query with two query paths, 
    ' one that returns order items and a second that returns the 
    ' billing and shipping addresses for each order.
    Dim query As ObjectQuery(Of SalesOrderHeader) = _
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address")
    
    // Create a SalesOrderHeader query with two query paths, 
    // one that returns order items and a second that returns the 
    // billing and shipping addresses for each order.
    ObjectQuery<SalesOrderHeader> query =
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address");
    
  • クエリ パスを使用すると、見かけ上は簡単なオブジェクト クエリのデータ ソースに対して複雑なコマンドが実行される可能性があります。これは、1 つのクエリ内の関連オブジェクトを返すには、1 つまたは複数の結合が必要になるために発生します。継承を備えたエンティティや多対多のリレーションシップを含んだパスなど、複雑な EDM に対してクエリを実行する場合は、さらに複雑になります。ObjectQuery によって生成されるコマンドを表示するには、ToTraceString メソッドを使用します。詳細については、「オブジェクト クエリ (Entity Framework)」を参照してください。クエリ パスに含まれる関連オブジェクトが多すぎる場合や、オブジェクトに含まれる行データが多すぎる場合、データ ソースはクエリを完了できないことがあります。これは、クエリでデータ ソースの機能を超える中間一時ストレージが必要になる場合に発生します。この場合は、関連オブジェクトを明示的に読み込むと、データ ソース クエリの複雑さを軽減できます。

詳細については、「クエリ パスを使用して結果を構築する方法 (Entity Framework)」を参照してください。

関連オブジェクトの明示的な読み込み

関連オブジェクトを明示的に読み込むには、ナビゲーション プロパティによって返される関連 End に対して Load メソッドを呼び出す必要があります。一対多のリレーションシップの場合は、EntityCollection に対して Load メソッドを呼び出します。一対一のリレーションシップの場合は、EntityReference に対して Load を呼び出します。これにより、関連オブジェクト データがオブジェクト コンテキストに読み込まれます。クエリからオブジェクトのコレクションが返されたら、そのコレクションを列挙して Load メソッドを呼び出し、SalesOrderHeader オブジェクトに属する各 SalesOrderDetail オブジェクトなど、コレクション内の各オブジェクトに対する関連オブジェクトを読み込むことができます。次の例では、SalesOrderDetail オブジェクトは指定の SalesOrderHeader オブジェクトに対して明示的に読み込まれています。

' Load the items for the order if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
    order.SalesOrderDetail.Load()
End If
// Load the items for the order if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
    order.SalesOrderDetail.Load();
}

[!メモ]

foreach (C#) または For Each (Visual Basic) 列挙で、Load メソッドを呼び出すと、Object Services は新たにデータ リーダーを開こうとします。この操作は、接続文字列で multipleactiveresultsets=true を指定して複数のアクティブな結果セットを有効にしていない限り失敗します。詳細については、MSDN の「複数のアクティブな結果セット (MARS) の使用」を参照してください。クエリの結果は、List コレクションに読み込むこともできます。この場合、データ リーダーが閉じ、コレクションを列挙して参照オブジェクトを読み込むことができます。

詳細については、「関連するオブジェクトを明示的に読み込む方法 (Entity Framework)」を参照してください。

関連オブジェクトのクエリ

EntityCollection クラスは IEnumerable インターフェイスを実装するため、LINQ を使用して、ナビゲーション プロパティによって返される EntityCollection に読み込まれるオブジェクトのコレクションに対してクエリを実行することができます。この操作は、クエリ パスを指定することでオブジェクトがオブジェクト コンテキストに明示的に読み込まれる場合でも、または Load メソッドを呼び出して明示的に読み込まれる場合でも関係なく正しく動作します。

EntityCollection に対して CreateSourceQuery メソッドを呼び出すと、オブジェクトをコレクションに最初に読み込まずに関連オブジェクトに対してクエリを実行できます。CreateSourceQueryObjectQuery を返し、実行時には、Load メソッドを呼び出す場合と同じオブジェクト セットを返します。クエリ ビルダ メソッドをこのオブジェクト クエリに適用すると、コレクションに読み込まれるオブジェクトをさらにフィルタ処理できます。詳細については、「EntityCollection 内の関連するオブジェクトにクエリを実行する方法 (Entity Framework)」を参照してください。

ObjectQuery はエンティティ オブジェクトとして EDM データを返します。ただし、クエリ投影にナビゲーション プロパティが含まれている場合、ObjectQuery は、関連オブジェクトを含んでいる入れ子になった DbDataRecord を返します。詳細については、「ナビゲーション プロパティを使用してリレーションシップをナビゲートする方法 (Entity Framework)」を参照してください。

参照

概念

オブジェクトとしてのデータのクエリ (Entity Framework)
オブジェクト クエリ (Entity Framework)
クエリ ビルダ メソッド (Entity Framework)