2017-07-02 44 views
0

我遇到了插入或更新約950個實體的問題。實體框架Core1.1 - 大容量插入或更新 - InvalidOperationException

var coins = JsonConvert.DeserializeObject<List<Currency>>(json); 
var sw = new Stopwatch(); 
sw.Start(); 
using (var ctx = CryptoContext.Get) 
{ 
    var existingCoins = ctx.Coins.ToList(); 
    foreach (var coin in coins) 
    { 
     var existing = existingCoins.FirstOrDefault(c => c.CMC_Id == coin.CMC_Id); 
     if (existing != null) 
     { 
      ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Modified; 
     } else 
     { 
      ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Added; 
     } 

    } 
    ctx.SaveChanges(); 
    var el = sw.ElapsedMilliseconds; 
} 

的代碼運行在我netcoreapp1.1的背景下,使用SQLite,並檢索貨幣的列表。 FluentScheduler每5分鐘完成一次。因爲它們不是完全大對象,所以我在內存中進行所有比較,並嘗試添加或更新每個對象。我的實體具有數據庫給定的ID ID,並且我從保證CMC_Id是唯一的API中檢索。

初始插入正常工作。第二個「更新」中出現錯誤。我相信發生了什麼事是我在跟蹤多個實體修改的每一個都具有0

一個id,我試圖按照這樣的:https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx

和錯誤我得到的是:"The instance of entity type 'Currency' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context."

我我不確定如何繼續更新每一行。

+0

我已經通過簡單的刪除所有的硬幣,然後重新添加,標誌着當種子編號過高而將其復位bandaided這個問題。該過程大約需要7秒。 –

回答

0

問題在這裏發現多個具有相同鍵的實體被跟蹤。

當您將EntityEntry.State設置爲某個值時,EF Core將開始跟蹤特定狀態下的實體。由於在您的代碼中,您正在查詢數據庫以找出現有實體,因此EF Core將開始使用給定密鑰跟蹤實體,因此在設置EntityEntry.State時會拋出異常,因爲已經存在具有相同密鑰的實體被跟蹤。

更準確地說,您正在嘗試AddOrUpdate。有多種方式來實現這一行爲。哪一個最好取決於添加一個沒有關係的實體或一個複雜的圖。

最簡單的方法是檢查是否存在,而不是從數據庫中跟蹤實體。選項是在查詢中使用AsNoTracking,以便EF不會開始跟蹤它。更優化的方式是從數據庫中獲取數量。如果您正在查詢PK屬性,則計數將爲0(不存在)或1(現有實體)。如果它不存在,則請致電Add,否則請致電Update

var updatedBlog = new Blog { Id = 1, Title = "Updated" }; 
var exist = db.Blogs.Count(b => b.Id == updatedBlog.Id) != 0; 
if (exist) 
{ 
    db.Update(updatedBlog); 
} 
else 
{ 
    db.Add(updatedBlog); 
} 
db.SaveChanges(); 

由於AddUpdate方法開始跟蹤整個圖形,如果你的圖是在一個一致的狀態,(所有實體是新的或所有正在修改),那麼它會工作得很好。

如果您的圖形有點不一致,那麼圖形中每個節點的狀態可能會不同(例如,更新博客,但它有新帖子)。那麼你應該在個人實體上使用EntityEntry.State。這確保該狀態僅適用於給定的實體,並且不適用於圖中的其他相關實體。儘管您需要對圖中的每個節點進行上述類型的檢查。另一種替代方法是使用Attach方法以Unchanged狀態附加整個圖形,然後爲單個節點設置狀態。

如果你有自動生成的鍵值,那麼你可能只有在更新時纔會設置PK值,否則它將是CLR默認值。對於沒有關係的單個實體,您可以檢查自己,而不是像上面的代碼那樣查詢數據庫並做出決定。對於圖表,您可以使用

db.ChangeTracker.TrackGraph(updatedBlog, n => n.Entry.State = n.Entry.IsKeySet ? EntityState.Modified : EntityState.Added); 

這將根據PK值是否設置來設置每個節點的狀態。

希望這有助於:)