繁体   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