3

このようなモデル優先のエンティティ フレームワーク設計があります (バージョン 4.4)

エンティティ ER 図 次のようなコードを使用してロードすると:

PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.FirstOrDefault(pss => pss.Id == snapshotId);

スナップショットは、DataInfo を除くすべて (つまり、SnapshotPart、Quote、QuoteType) をロードしました。SQL を調べると、0..1 の関係により、Quote に DataInfo への FK がないことが原因のようです。ただし、Quote のナビゲーション プロパティ 'DataInfo' は、データベースにアクセスしてフェッチすることを期待していました。

私の現在の回避策はこれです:

foreach (var quote in snapshot.ComponentQuotes)
{
    var dataInfo = db.DataInfoes.FirstOrDefault(di => di.Quote.Id == quote.InstrumentQuote.Id);
    quote.InstrumentQuote.DataInfo = dataInfo;
}

これを達成するためのより良い方法はありますか?EF が参照を自動的に読み込むと思いましたか?

4

1 に答える 1

2

この問題は、基本的な linq 構築ブロックが Entity Framework と対話する方法に関係しています。

次の(疑似)コードを使用します。

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000);
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

これは問題ないように見えますが、IQueryable というインターフェイスが原因で実行時エラーがスローされます。

IQueryableはIEnumerableを実装し、式とプロバイダーの情報を追加します。これにより、基本的に、データベースに対して sql ステートメントを作成して実行することができ、IEnumerable のようにデータをフェッチしてそれらを反復するときにテーブル全体をロードする必要がなくなります。

linq は、使用されるまで式の実行を延期するため、IQueryable 式を SQL にコンパイルし、必要になる直前にのみデータベース クエリを実行します。Where()これにより処理速度が大幅に向上し、 orSelect()が実行されるたびにデータベースにアクセスすることなく、式の連鎖が可能になります。副作用として、オブジェクトが db のスコープ外で使用されると、db が破棄された後に sql ステートメントが実行されます。

linq を強制的に実行するには、次のように ToList を使用できます。

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000).ToList();
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

これにより、linq は db に対して式を実行し、数が 1000 を超えるすべてのアドレスを取得するようになります。これは、addresses テーブル内のフィールドにアクセスする必要がある場合は問題ありませんが、都市の名前を取得したいので (あなたと同様の 1..1 の関係)、実行する前に別のバンプにぶつかります。遅延読み込み。

エンティティ フレームワークはデフォルトでエンティティを遅延ロードするため、必要になるまでデータベースから何もフェッチされません。繰り返しになりますが、これがないと、データベースへのすべての呼び出しでデータベース全体がメモリに取り込まれる可能性があるため、処理が大幅に高速化されます。しかし、利用可能なコンテキストに依存するという問題があります。

EF を熱心な読み込みに設定することもできます(モデルで、プロパティに移動し、[Lazy Loading Enabled] を False に設定します) が、おそらく使用しない多くの情報がもたらされます。

この問題の最善の解決策は、db のスコープ内ですべてを実行することです。

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000);
    addresses.Select(addr => Console.WriteLine(addr.City.Name));
}

これは非常に単純な例ですが、実際には ninject のような DI コンテナーを使用して依存関係を処理し、アプリの実行中にデータベースを利用できるようにすることができます。

これにより、Includeが残ります。Include は、SQL ステートメントを構築するときに、指定されたすべての関係パスを IQueryable に含めます。

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Include("City").Where(addr => addr.Number > 1000).ToList;
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

これは機能し、データベース全体をロードする必要があることと、DI をサポートするためにプロジェクト全体をリファクタリングする必要があることの間の適切な妥協点です。

他にできることは、複数のテーブルを単一のエンティティにマップすることです。あなたの場合、関係は 1-0..1 であるため、問題はありません。

于 2013-10-02T16:17:19.303 に答える