簡體   English   中英

Entity Framework Core DbContext SaveChanges 在拋出 DbUpdateConcurrencyException 時是否隱式調用 DetectChanges?

[英]Does Entity Framework Core DbContext SaveChanges call DetectChanges implicitly when it throws DbUpdateConcurrencyException?

作為一個場景,一個實體有一個 xmin 並發檢查,它相當於 PostgreSQL 數據庫中的RowVersion列。 在更新這個實體時,有一個類似下面的循環來強制更新操作作為獲勝策略的最后一個。

var entities = entityListContainsEntitiesModifiedByUser;
using var context = new ExampleDbContext();

while(true) 
{
    try
    {
        var entitiesWillBeUpdated = context.Set<TEntity>().Where(x => !entities.Contains(x)).ToList();
        context.UpdateRange(entitiesWillBeUpdated);
  
        // Some of entities have deleted since round before.
        // (This line will be mentioned later below)     
        context.ChangeTracker.DetectChanges();

        var revertEntities = context.ChangeTracker.Entries<TEntity>().Where(x => !entitiesWillBeUpdated.Contains(x.Entity)).ToList();
  
        foreach(var revert in reverEntities) 
        {
            revert.State == EntityState.Detach;
        }

        context.SaveChanges();
        break;
    }
    catch(DbUpdateConcurrencyException e) 
    {
        var entry = e.Entries.Single();
        var dbval = await entry.GetDatabaseValues(); 

        if (dbval == null) 
        {
            // Updating entry has been deleted so it cant be modified.  Ignore it.
            entry.State = EntityState.Detached;
            continue;
        }

        // Updating entry has been concurrently updated by someone else so change the old version to the new one
        var pxmin = entry.Property("xmin")!;
        pxmin.CurrentValue =  dbval.GetValue<uint>("xmin");
        pxmin.OriginalValue =  dbval.GetValue<uint>("xmin");
    }
}

如果我們一步一步來,假設用戶修改了 1000 個實體,我們想要更新這些。 在嘗試塊中,所有 1000 個實體都被跟蹤並設置為已修改。 此時有 0 個 revertEntities,因為這是第一輪,因此之前沒有從數據庫中獲取跟蹤實體。 然后,調用SaveChanges 在 EF Core 更新這些實體時,其他用戶同時在這 1000 個實體中修改了 1 個實體。

因此捕獲了DbUpdateConcurrencyException 在 catch 塊中設置了這個 1 實體的新 xmin 版本。 在再次嘗試更新所有 1000 條記錄之前,有一段時間,假設在這段時間內,1000 條記錄中的 300 條記錄被刪除。 在下一輪,entitiesWillBeUpdated 有 700 條記錄, ExampleDbContect跟蹤前一輪加載的 1000 個實體,其中 300 個從數據庫中刪除,因此 revertEntities 里面有 300 個實體。

這 300 個實體被設置為分離並調用SaveChanges

此時,另一個用戶再次修改了 700 個實體中的 1 個。 在下一輪中,這 1 個修改實體的 xmin 版本被更新。

問題是:

此時, revertEntities是否會再次具有 300 個分離的實體,或者它們是否從ExampleDbContext中清除並且由於之前進行了一輪SaveChanges調用而未被跟蹤,所以它內部是否會有 0 個實體?

我知道 SaveChanges 隱式調用context.ChangeTracker.DetectChanges()但如果拋出DbUpdateConcurrencyException怎么辦?

我知道,當拋出此異常時,內存中的狀態設置為已修改的跟蹤實體值不會更改,但如果沒有異常,則此實體值具有與更新的數據庫中的記錄相同的新值。 對於分離的實體也是如此嗎?

通常,如果沒有引發錯誤,所有這些都將從ExampleDbContext中清除並且不會被跟蹤。 那么在最后一種情況下,分離的實體是否被清除或它們仍然存在於本地? 我需要像代碼中的注釋行一樣手動調用DetectChanges()嗎?

我在另一個項目中測試了這個案例,我看到,在分離跟蹤條目並啟用 AutoDetectChanges(默認情況下啟用)時,條目會在Entry.State = EntryState.Detached行之后立即從 ChangeTracker 的條目列表中刪除。 因此,無需等待 SaveChanges 或 DetectChanges 調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM