2015-01-12 105 views
6

我有什麼似乎是一個常見問題,但我不知道如何實現預期的結果。我有一個定義了導航屬性的嵌套實體,如下圖所示。實體框架,批量插入和維護關係

enter image description here

地圖點收集有可能成爲一個給定MapLine非常大,可以有相當大量MapLines的MapLayer的。

這裏的問題是什麼是使用實體框架將MapLayer對象插入到數據庫中並保持由導航屬性定義的關係的最佳方法?

標準的實體框架實現

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

導致大量內存棘返回時間相當差。

我曾嘗試實施EntityFramework.BulkInsert packagebut it does not honor the relationships of the objects.

這似乎將是某人以前曾經遇到一個問題,但我似乎無法找到解釋如何完成這個任務的任何資源。

更新

我試圖實現由Richard提供的建議,但我不理解我怎麼會去這個嵌套的實體,如我所描述的一個。我假設我需要插入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作爲一個實體並存儲在MapLines => MapPoints關係如在原始JSON串MapLayer實體上的一個字節數組(因爲我沒有查詢這些結構對我有用)。

俗話說「它不漂亮,但它有效」。

我確實對BulkInsert包和管理EF外部的關係有了一些成功,但在嘗試使用EF將數據拉回到系統時又遇到了內存問題。目前看來,EF無法有效處理大型數據集和複雜關係。

+0

你能解釋你在用數據後綴做什麼嗎?我感興趣的是,如果僅僅爲數據使用簡單的二進制格式不是更好。 –

回答

14

我對壞境的保存很糟糕。所有這些關於將迭代中保存100行,1000行,然後處理上下文或清除列表和分離對象,將空值賦予所有內容等的建議等等 - 這些都是廢話。我們有要求在每個表中插入數百萬行。在這些情況下絕對不應該使用實體。迭代過程中,您將與內存泄漏並降低插入速度。

我們的第一個改進是創建存儲過程並將它們添加到模型中。它比Context.SaveChanges()快100倍,並且沒有泄漏,速度也不會隨着時間而下降。我們決定使用SqlBulkCopy。它超快速。使用存儲過程的速度要快1000倍。

所以我的建議是: 如果你有很多行要插入但count是在50000行之下,使用存儲過程,導入模型; 如果您有數十萬行,請嘗試SqlBulkCopy

下面是一些代碼:

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(); 

如果使用DbTransaction具有上下文,可以管理批量插入使用該交易爲好,但它需要一些黑客。

+0

「對於大量的上下文保存,我的經驗很差,所有這些關於將迭代保存爲100行,1000行,然後處理上下文或清除列表和分離對象,將空值賦予所有內容等的建議等等 - 這些都是廢話。 - 閱讀直到這一點,+1已經 –

+0

但關係問題呢? 他將如何解決這個問題? –

+1

@eranotzap,如果你的意思是在批量插入時使用relashinship,我們只在父表中添加了2個額外的列並將其填充到代碼中。 1表示部分稱爲PortionID,其他表示關係表示Relation RelationID。批量插入後,我們按部分選擇數據,並選擇ID和RelationID。所以我現在有關係,並分配適當的ID給子記錄,通過RelationID進行比較並從db分配ID。然後我爲孩子做另一個bulkinsert。 –

6

批量插入不是使用實體框架高效添加數據的唯一方式 - this answer中詳細介紹了多種替代方法。你可以使用那裏建議的優化(禁用更改跟蹤),然後你可以添加正常的東西。

請注意,由於您一次添加多個項目,因此需要相當頻繁地重新創建上下文,以防止內存泄漏和放緩。