2017-04-04 4 views
2

.Includeなどを使用して読み込みに関するさまざまな質問がありましたが、SQLフォームのように巨大な結合にクエリすると、情報は、その顧客が1000件を所有し、私はアイテムの情報ではなく、1つの顧客レコードやマルチテーブルセット1000個のアイテムと一緒に複製され、同じ顧客情報の1000行を取得しますEntity Framework - コレクション内のメンバーの特定のナビゲーションプロパティをロードする

context.Customers.Include(c=> c.Inventory).Where(c=>c.ID == id); 
//side note, WHY can't I use .Find() after Include? 

を行います。これは実際には非効率的で、実際には遅いクエリにつながります。

だから私は、お客様の設定している場合:

//get all customers in TX 
var texasCustomers = context.Customers.Where(c=> c.State == "TX"); 

をそして私はXLSXする輸出でそれらをループにしたい:これは「在庫」テーブルにクエリを生成

foreach (var c in texasCustomers) { 
    //compile row per item SKU 
    foreach(var sku in c.Inventory.GroupBy(i=>i.SKU)) { 
    xlsx.SetCellValue(row, col, c.Name); 
    //output more customer info here 
    xlsx.SetCellValue(row, col, sku.Key); 
    xlsx.SetCellValue(row, col, sku.Sum(i=>i.Quantity)); 
    } 
} 

PERの顧客。これは高速なクエリですが、SAMEクエリを1000回実行すると、面倒に遅くなります。

だから私はこのようなことをやった:

//get customer key 
var customerids = texasCustomers.Select(c=> c.ID).ToArray(); 
var inventories = context.Inventories.Where(i=> customerids.Contains(i.CustomerID)).ToList(); 

を...そして今、私の輸出ループはもっとこのようになります...最初の例からNAVプロパティで動作する内部ループは、内となりインベントリオブジェクトの構築済みリストに対して-memory LINQフィルタ:

foreach (var c in texasCustomers) { 
    //compile row per item SKU 
    foreach(var sku in inventories.Where(i=> i.CustomerID == c.ID)) { 
    xlsx.SetCellValue(row, col, c.Name); 
    //output more customer info and then the sku info 
    xlsx.SetCellValue(row, col, sku.Key); 
    xlsx.SetCellValue(row, col, sku.Sum(i=>i.Quantity)); 
    } 
} 

これが成功した「クエリループごとに」問題回避しますが、明らかな欠点を持っている...そして、ちょうど間違って感じています。

私は何が欠けていますか?一度でコレクションのメンバーのナビゲーションプロパティのすべてを「埋める」ために

texasCustomers.LoadAll(c=> c.Inventories); 

:私はのような何かをやらせる秘密のEF機能はどこですか?あるいは私は間違った角度から問題に近づいていますか?

EFが単一の巨大な非正規化テーブルに変換されないSQLを生成するようにクエリを構築する方法はありますか?

答えて

2

あなたは正確に何をしたい行うことができます秘密EF機能はありませんが、関連企業がある場合でも、Includeせずにマテリアライズド・エンティティのナビゲーションプロパティを移入し、を修正近いナビゲーションプロパティと呼ばれるものがありますすでにコンテキストにロードされています。

したがって、あなたが最初に次のように関連の在庫をロードすることができます。make、

foreach (var c in texasCustomers) 
{ 
    var inventories = c.Inventories; // must be there 
    // ... 
} 

しかし、ナビゲーションプロパティにアクセスする際に遅延読み込みを避けるために:

texasCustomers.SelectMany(c => c.Inventories).Load(); 

をしてから、メインクエリを実行し、反復上記のすべてを実行する前に、遅延ロードが無効になっていることを確認してください。

context.Configuration.ProxyCreationEnabled = false; 

上記の手法では、関連するエンティティがない場合、通常の使用方法と同じように空のリストを返すのではなく、ナビゲーションプロパティはnullのままであるため、nullのチェックまたは使用を必ずチェックしてくださいアクセスすると?? Enumerable.Empty<Inventory>()になります。

+0

ああ、これはもっと良い感じです。これを打つつもりです。ありがとう。 – BLSully

+0

素晴らしい情報!これは私にその道の90%を得ました。私はまだ理解していない(例えば、 '.Find()'から作業していないのですが、処理されていない '.AsQueryable()'を与えると動作しますか?私はEFデータベースのログを4MEGAバイトから7KBにエクスポートしました(4つのバルククエリ対1000x3の断片的なもの) – BLSully

+0

確かに、これは 'IQueryable'でのみ動作します。 'Find'については、ロード関連のデータメソッドのほとんどが直接それと連動しないので、単純な' Where'や 'FirstOrDefault'などを使用してください。単一のエンティティの場合は、 [Find]をクリックし、ナビゲーションコレクションのプロパティの[明示的な読み込み](https://msdn.microsoft.com/en-us/library/jj574232(v=11113).aspx#Anchor_2)を使用します。 Btw、私の答えで使用される手法は、関連するエンティティをリンクから明示的にロードする際に**フィルタを適用するという変形です**セクション。 –

0

実際、私はそれを分けておくべきだと思います。

お客様のための1つのテーブルと、もう1つのオーダーのテーブルと、もう1つのOrderItensへのテーブル。

注文は頭とOrderItens体(このようなもの)

をEと、それとの関係を行います。

その後、すべての子をメモリにロードしてFirst()関数を適用する必要があります。

関連する問題