簡體   English   中英

EF Core 6“正常”更新方法不尊重 RowVersion 預期行為?

[英]EF Core 6 "normal" update method doesn't respect RowVersion expected behavior?

我有一個 .NET6 API 項目,它允許用戶從數據庫(SQL Server)中獲取資源,並在 Web 客戶端上更新它們,然后提交更新的資源以保存到 db。 如果另一個用戶在編輯期間已經更新了相同的資源,我需要通知用戶。 我嘗試使用 EF IsRowVersion 屬性進行此並發檢查。

我注意到“正常”更新過程(只是獲取實體、更改屬性和保存)不尊重 RowVersion 預期行為。 但是,如果我使用 AsNoTracking 獲取實體並使用 db.Update 方法,並發檢查將按預期工作。 可能是什么原因,db.Update 是強制 RowVersion 檢查的唯一方法嗎? 該方法的缺點是它會嘗試更新每個屬性,而不僅僅是那些已更改的屬性。 下面是簡化且可運行的控制台應用程序示例:

using Microsoft.EntityFrameworkCore;
Guid guid;
using (PeopleContext db = new())
{
    Person p = new() { Name = "EF", Age = 30 };
    db.Database.EnsureDeleted();
    db.Database.EnsureCreated();
    db.People.Add(p);
    await db.SaveChangesAsync();
    guid = p.Id;
}
using (PeopleContext db = new())
{
    Person p = await db.People.FirstAsync(x => x.Id == guid);
    p.Name = "FE";
    p.RowVersion = Convert.FromBase64String("AAAAAADDC9I=");
    await db.SaveChangesAsync(); // Does not throw even though RowVersion is incorrect
}
using (PeopleContext db = new())
{
    Person p = await db.People.AsNoTracking().FirstAsync(x => x.Id == guid);
    p.Name = "EFFE";
    p.RowVersion = Convert.FromBase64String("AAAAAAGGC9I=");
    db.People.Update(p);
    await db.SaveChangesAsync(); // Throws DbUpdateConcurrencyException as expected, but updates all properties
}
public class Person
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int Age { get; set; }
    public byte[] RowVersion { get; set; } = Array.Empty<byte>();
}

public class PeopleContext : DbContext
{
    public PeopleContext(){}

    public DbSet<Person> People => Set<Person>();
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=EFRowVersionDb;Integrated Security=True;");
        optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
        optionsBuilder.EnableSensitiveDataLogging();

    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>(entity =>
        {
            entity.Property(e => e.RowVersion)
                .IsRequired()
                .IsRowVersion();
        });
    }
}

我通過覆蓋這樣的 SaveChangesAsync 方法解決了這個問題:

    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
    {
        foreach (var item in ChangeTracker.Entries().Where(x=>x.State == EntityState.Modified))
        {
            item.OriginalValues["RowVersion"] = item.CurrentValues["RowVersion"];
        }
        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

我重寫了那個簽名方法,導致沒有布爾值的那個方法調用了那個方法。 同步版本也是一樣。

暫無
暫無

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

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