関連オブジェクトの読み込み (Entity Framework)
このトピックでは、関連エンティティの読み込みに使用できるパターンについて説明します。 エンティティ型は、データ モデルにおけるアソシエーションを表すナビゲーション プロパティを定義できます。 これらのプロパティを使用して、定義されたアソシエーションで返されたエンティティに関連するエンティティを読み込むことができます。 エンティティがデータ モデルに基づいて生成されると、両方のアソシエーション End にあるエンティティに対してナビゲーション プロパティが生成されます。 これらのナビゲーション プロパティは、一対一または多対一のリレーションシップの "一" の側では参照を、一対多または多対多のリレーションシップの "多" の側ではコレクションを返します。 詳細については、「ナビゲーション プロパティ」および「リレーションシップの定義と管理 (Entity Framework)」を参照してください。
次のパターンでは、関連エンティティを読み込む方法について説明します。
読み込みパターン | 説明 |
---|---|
クエリで指定する |
ナビゲーション プロパティを使用すると、リレーションシップを明示的にナビゲートする Entity SQL または LINQ to Entities クエリを作成できます。 このようなクエリを実行すると、クエリの最も外側のプロジェクションにナビゲーション プロパティとして含まれる関連エンティティが返されます。 詳細については、次のトピックを参照してください。 |
明示的読み込み |
エンティティを ObjectContext に明示的に読み込む場合は、データベースへの複数のラウンドトリップが必要になり、複数の有効な結果セットも必要になることがありますが、返されるデータ量は読み込み中のエンティティのみに制限されています。 EntityCollection または EntityReference に対する Load メソッド、あるいは、ObjectContext に対する LoadProperty メソッドを使用すると、データ ソースから関連エンティティを明示的に取得できます。 Load メソッドを呼び出すたびに、関連情報を取得するためのデータベースへの接続が開きます。 つまり、関連エンティティが明示的に要求されない限り、クエリは実行されません。 明示的読み込みは、Entity Framework の既定の動作です。
注 :
Load を呼び出す以前に、関連エンティティに関する一部の情報は既に ObjectContext に読み込まれています。
詳細については、このトピックの「Explicitly Loading Related Objects」セクションを参照してください。 |
遅延読み込み |
この種類の読み込みでは、関連エンティティは、ナビゲーション プロパティへのアクセス時に、データ ソースから自動的に読み込まれます。 この種類の読み込みでは、エンティティがまだ ObjectContext にない場合、アクセスするナビゲーション プロパティごとに、データ ソースに対して個別のクエリが実行されます。 詳細については、このトピックの「Lazy Loading」セクションを参照してください。 |
一括読み込み または Include でのクエリ パスの定義 |
アプリケーションで必要な関連エンティティのグラフの正確な形状がわかっている場合は、ObjectQuery に対して Include メソッドを使用して、最初のクエリの一部としてどの関連エンティティを返すかを制御するクエリ パスを定義できます。 クエリ パスを定義すると、データベースに対して 1 回要求するだけで、パスに定義されているすべてのエンティティが 1 つの結果セットとして返され、さらに、パスに指定されている型のすべての関連エンティティが、クエリから返される各オブジェクトに読み込まれます。 詳細については、このトピックの「Defining a Query Path to Shape Query Results」セクションを参照してください。 |
関連エンティティ オブジェクトの明示的な読み込み
関連エンティティを明示的に読み込むには、ナビゲーション プロパティによって返される関連 End に対して Load メソッドを呼び出す必要があります。 一対多のリレーションシップの場合は、EntityCollection に対して Load メソッドを呼び出します。一対一のリレーションシップの場合は、EntityReference に対して Load メソッドを呼び出します。 POCO エンティティを操作する場合は、ObjectContext に対して LoadProperty メソッドを使用します。 詳細については、「関連 POCO エンティティの読み込み (Entity Framework)」を参照してください。 LoadProperty メソッドは、EntityObject から派生するエンティティにも使用できます。 これらのメソッドでは、関連オブジェクト データがオブジェクト コンテキストに読み込まれます。 クエリから結果が返されたら、foreach ループ (Visual Basic では For Each...Next) を使用してオブジェクトのコレクションを列挙し、結果の各エンティティの EntityReference プロパティと EntityCollection プロパティに対して Load メソッドを条件付きで呼び出すことができます。
注 : |
---|
foreach (C#) または For Each (Visual Basic) 列挙で Load メソッドを呼び出すと、Entity Framework は新たにデータ リーダーを開こうとします。この操作は、接続文字列で multipleactiveresultsets=true を指定して複数のアクティブな結果セットを有効にしていない限り失敗します。詳細については、MSDN の「複数のアクティブな結果セット (MARS) の使用」を参照してください。クエリの結果は、List コレクションに読み込むこともできます。この場合、データ リーダーが閉じ、コレクションを列挙して参照エンティティを読み込むことができます。 |
詳細については、「方法: 関連するオブジェクトを明示的に読み込む (Entity Framework)」を参照してください。
クエリ結果を構造化するためのクエリ パスの定義
クエリ パスを指定するには、オブジェクト グラフの文字列表記を ObjectQuery の Include メソッドに渡します。 このパスは、オブジェクト クエリの実行時に返す関連エンティティを指定します。 たとえば、Contact オブジェクトに対するクエリで定義されたクエリ パスにより、関連する各 SalesOrderHeader および SalesOrderDetail が必ず返されます。 このクエリを次に示します。
' Define a LINQ query with a path that returns
' orders and items for a contact.
Dim contacts = (From contact In context.Contacts.Include("SalesOrderHeaders.SalesOrderDetails") _
Select contact).FirstOrDefault()
// Define a LINQ query with a path that returns
// orders and items for a contact.
var contacts = (from contact in context.Contacts
.Include("SalesOrderHeaders.SalesOrderDetails")
select contact).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.SalesOrderHeaders.Include("SalesOrderDetails").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.SalesOrderHeaders.Include("SalesOrderDetails").Include("Address");
クエリ パスを使用すると、見かけ上は簡単なオブジェクト クエリのデータ ソースに対して複雑なコマンドが実行される可能性があります。 これが発生するのは、複数の関連オブジェクトを 1 つのクエリで返すには、1 つ以上の結合が必要になるためです。その結果、データ ソースから返される各関連エンティティに対してデータが冗長になります。 継承のあるエンティティや多対多のリレーションシップを含んだパスなど、複雑なモデルに対してクエリを実行する場合は、さらに複雑になります。 ObjectQuery によって生成されるコマンドを表示するには、ToTraceString メソッドを使用します。 クエリ パスに含まれる関連オブジェクトが多すぎる場合や、オブジェクトに含まれる行データが多すぎる場合、データ ソースはクエリを完了できないことがあります。 これは、クエリでデータ ソースの機能を超える中間一時ストレージが必要になる場合に発生します。 この場合は、関連オブジェクトを明示的に読み込むか遅延読み込みを有効にすると、データ ソース クエリの複雑さを軽減できます。 複雑なクエリを最適化しても頻繁にタイムアウトする場合は、CommandTimeout プロパティの設定でタイムアウト値を大きくすることを検討してください。
詳細については、「方法: クエリ パスを使用して結果を構築する (Entity Framework)」を参照してください。
エンティティ オブジェクトの遅延読み込み
Entity Framework は、関連エンティティの遅延読み込みをサポートします。 Entity Framework ランタイムで、ObjectContext のインスタンスの LazyLoadingEnabled プロパティの既定値は、false です。 ただし、Entity Framework ツールを使用して新しいモデルおよび対応する生成済みクラスを作成する場合、オブジェクト コンテキストのコンストラクターにおける LazyLoadingEnabled は true に設定されます。 遅延読み込みが有効になっている場合、関連エンティティは、ナビゲーション プロパティの get アクセサーによってプログラムでアクセスされるまで、データ ソースから読み込まれません。 遅延読み込みを無効にするには、System.Data.Objects.ObjectContext.ContextOptions プロパティによって返される ObjectContextOptions のインスタンス上で、LazyLoadingEnabled プロパティを false に設定します。
遅延読み込みは、一括読み込みと併用することができます。 この方法で、ベース データ グラフをクエリ パスを使用して定義することができ、元のクエリ パスに含まれない追加の関連エンティティを必要に応じて読み込むことができます。 詳細については、「方法: 遅延読み込みを使用して関連するオブジェクトを読み込む (Entity Framework)」を参照してください。
遅延読み込みを使用する際には、次のことを考慮する必要があります。
遅延読み込みは、単一エンティティ (EntityReference など) とエンティティのコレクション (EntityCollection など) の両方を返すナビゲーション プロパティでサポートされます。
遅延読み込みが有効であっても、関連エンティティが既に読み込まれているなら、関連オブジェクトが再度読み込まれることはありません。
遅延読み込みは、Detached 状態にあるエンティティでサポートされます。 この場合、関連オブジェクトも Detached 状態で返されます。
遅延読み込みの動作は、ObjectContext インスタンスによって決まります。このインスタンスは、(エンティティが NoTracking MergeOption で読み込まれた場合でも) オブジェクトをデータ ソースから、あるいはオブジェクトの追加先またはアタッチ先から取得するために使用されます。 その結果、遅延読み込みの動作は、このコンテキストが廃棄された後には変更できず、それ以降の遅延読み込み操作は失敗します。
エンティティをシリアル化する際には、遅延読み込みを無効にすることを検討してください。 そうしないと、遅延読み込みが起動され、シリアル化されたエンティティに想定以上のデータが格納されることがあります。
POCO エンティティに遅延読み込みを使用する場合は、さらに考慮が必要な点があります。 詳細については、「関連 POCO エンティティの読み込み (Entity Framework)」を参照してください。
関連エンティティ オブジェクトのクエリ
EntityCollection に対して CreateSourceQuery メソッドを呼び出すと、先にオブジェクトをコレクションに読み込まなくても、関連オブジェクトを照会することができます。 CreateSourceQuery は、ObjectQuery を返します。つまり、Load メソッドを呼び出した場合と同じオブジェクトのセットが実行時に返されるということです。 クエリ ビルダー メソッドをこのオブジェクト クエリに適用すると、コレクションに読み込まれるオブジェクトをさらにフィルター処理できます。 詳細については、「EntityCollection 内の関連するオブジェクトにクエリを実行する方法 (Entity Framework)」を参照してください。
ObjectQuery は、エンティティ データをエンティティとして返します。 ただし、最も外側のクエリ プロジェクションにナビゲーション プロパティが含まれている場合、クエリはナビゲーションによってアクセスされる関連エンティティも返します。 詳細については、「ナビゲーション プロパティを使用してリレーションシップをナビゲートする方法 (Entity Framework)」を参照してください。
パフォーマンスに関する考慮事項
関連エンティティの読み込みパターンを選択する場合、1 つのクエリで返されるデータの量およびその複雑性と、データ ソースへの接続の数やタイミングを対比して、各手法の動作を検討してください。 一括読み込みでは、1 つのクエリで、クエリされたエンティティと共にすべての関連エンティティが返されます。 これは、データ ソースへの接続は 1 つだけですが、最初のクエリで多量のデータが返されることを意味します。 さらに、データ ソースに対して実行されるクエリで追加の結合が必要になるので、多数のクエリ パスによってクエリがより複雑になります。
明示的読み込みと遅延読み込みでは、関連オブジェクト データが実際に必要になるまで、そのデータの要求を遅延することができます。 これにより、最初のクエリの複雑性が軽減されますが、返されるデータは完全なデータではなくなります。 ただし、続いて関連オブジェクトを読み込むたびに、データ ソースへの接続が確立され、クエリが実行されます。 遅延読み込みでは、ナビゲーション プロパティへのアクセス時に、関連エンティティがまだ読み込まれていなかった場合にのみ、この接続が実行されます。 初期クエリによってどの関連エンティティが返されるかという点や、関連エンティティがデータ ソースから読み込まれるタイミングの管理が重要である場合は、遅延読み込みを無効にすることを検討してください。 遅延読み込みは、Entity Framework によって生成されたオブジェクト コンテキストのコンストラクターでは有効になっています。
詳細については、「パフォーマンスに関する考慮事項 (Entity Framework)」を参照してください。
参照
概念
概念モデルに対するクエリ (Entity Framework)
オブジェクト クエリ (Entity Framework)
クエリ ビルダー メソッド (Entity Framework)