2015-01-12 10 views
6

私は共通の問題であるように思えますが、希望の結果を達成する方法を理解することはできません。次の図に示すように、ナビゲーションプロパティが定義された入れ子のエンティティがあります。エンティティフレームワーク、一括挿入、およびリレーションシップの維持

enter image description here

マップ点の集合は、潜在的に、所与のMapLineため非常に大きくすることができ、にMapLayerためMapLinesのかなり多数が存在し得ます。

ここで問題となるのは、MapLayerオブジェクトをEntity Frameworkを使用してデータベースに挿入し、ナビゲーションプロパティで定義された関係を維持するための最良の方法です。

標準のEntity Frameworkの実装

dbContext.MapLayers.Add(mapLayer); 
dbContext.SaveChanges(); 

は、大容量メモリのスパイクとかなり貧弱なリターン時間が発生します。

私は誰かが前に遭遇したことが問題になりますが、私はこのタスクを実行する方法を説明任意のリソースを見つけるように見えるカントのようにこれはそうEntityFramework.BulkInsert packagebut it does not honor the relationships of the objects.

を実装しようとしています。

アップデートは

私はリチャードが提供する提案を実装しようとしたが、私は、そのような私が説明した一人として、ネストされたエンティティのためにこれについて行くだろうか理解していないのです。私は、MapLayerオブジェクト、次にMapLines、MapPointsを挿入してデータベースのPF/FK関係を尊重する必要があるという前提の下で実行しています。私は現在、次のコードを試していますが、これは正しいとは思われません。これを達成するために、複数の異なる方法を試した後

dbContext.MapLayers.Add(mapLayer); 
dbContext.SaveChanges(); 

List<MapLine> mapLines = new List<MapLine>(); 
List<MapPoint> mapPoints = new List<MapPoint>(); 
foreach (MapLine mapLine in mapLayer.MapLines) 
{ 
    //Update the mapPoints.MapLine properties to reflect the current line object 
    var updatedLines = mapLine.MapPoints.Select(x => { x.MapLine = mapLine; return x; }).ToList(); 

    mapLines.AddRange(updatedLines); 
} 

using (TransactionScope scope = new TransactionScope()) 
{ 
    MyDbContext context = null; 
    try 
    { 
     context = new MyDbContext(); 
     context.Configuration.AutoDetectChangesEnabled = false; 

     int count = 0; 
     foreach (var entityToInsert in mapLines) 
     { 
      ++count; 
      context = AddToContext(context, entityToInsert, count, 100, true); 
     } 

     context.SaveChanges(); 
    } 
    finally 
    { 
     if (context != null) 
      context.Dispose(); 
    } 

    scope.Complete(); 
} 

アップデート2

私は最終的にあきらめ、ちょうどエンティティようにMapLayerを挿入し、内生のJSON文字列としてMapLines => MapPoints関係が記憶されていますMapLayerエンティティのバイト配列(これは私にとってはうまく機能する構造に対してクエリを実行していません)。

「いいですね、でもうまくいきます。

BulkInsertパッケージとEF以外の関係を管理することで成功しましたが、EFを使用してデータをシステムに戻そうとするとメモリの問題が発生しました。現在、EFは大規模なデータセットと複雑な関係を効率的に処理できません。

+0

データの後に何をしているのか説明できますか?あなたのデータにシンプルなバイナリフォーマットを使用するほうが良いのではないかと心配しています。 –

答えて

14

私は巨大なコンテキストセーブで悪い経験をしていました。100行、1000行の繰り返しで保存し、コンテキストを破棄したり、リストを消去したり、オブジェクトを切り離したり、すべてにnullを割り当てるなどの推奨事項はすべて虚偽です。私たちは、多くのテーブルに毎日何百万もの行を挿入する必要がありました。間違いなく、これらの条件でエンティティを使用すべきではありません。反復処理が進行すると、メモリリークや挿入速度の低下を防ぐことができます。

最初の改善点は、ストアドプロシージャを作成してモデルに追加することでした。 Context.SaveChanges()より100倍速く、リークはなく、時間の経過とともに速度が低下することはありません。

しかし、それは十分ではなく、SqlBulkCopyを使用することに決めました。超高速です。ストアドプロシージャを使用して1000倍高速になります。

私の提案は次のようになります: 挿入する行がたくさんありますが、カウントが50000行のようなものの場合は、モデルにインポートされたストアドプロシージャを使用します。 数十万行がある場合は、SqlBulkCopyを試してみてください。ここで

はいくつかのコードです:あなたはコンテキストにDbTransactionを使用する場合は

EntityConnection ec = (EntityConnection)Context.Connection; 
SqlConnection sc = (SqlConnection)ec.StoreConnection; 

var copy = new SqlBulkCopy(sc, SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.Default , null); 

copy.DestinationTableName = "TableName"; 
copy.ColumnMappings.Add("SourceColumn", "DBColumn"); 
copy.WriteToServer(dataTable); 
copy.Close(); 

、あなたにもそのトランザクションを使用して一括挿入に管理することができますが、それはいくつかのハックを必要とします。

+0

"巨大なコンテキストセーブで悪い経験がありました.100行、1000行の繰り返しで保存し、コンテキストを破棄したり、リストを消去したり、オブジェクトを切り離したり、すべてにnullを割り当てたりすることについての推奨事項はすべてありません。 - この時点まで読んで+1を既に –

+0

しかし、関係の問題はどうですか? 彼はそれをどうやって解決しますか? –

+1

@eranotzap、一括挿入中にrelashinshipを意味する場合は、親テーブルに2つの列を追加して、コードに記入しました。 1は部分的にはPortionID、他の部分はRelationIDのためのものです。バルク挿入後、データを部分ごとに選択し、IDとRelationIDを選択します。だから私は今関係を持って、RelationIDとdbからIDを割り当てることによって比較する子レコードに適切なIDを割り当てます。それから私は子供のために別の一括挿入を行います。 –

6

Bulk Insertは、Entity Frameworkを使用して効率的にデータを追加する唯一の方法ではありません。多くの代替方法がthis answerで詳しく説明されています。そこに提案されている最適化を使用することができます(変更トラッキングを無効にする)。

多くのアイテムを一度に追加する場合は、メモリリークを防ぐために頻繁にコンテキストを再作成し、遅くなる必要があることに注意してください。

関連する問題