使用EF Core和MySQL实现行版本的更好方法是什么?

Jef*_*eff 5 c# mysql entity-framework-core asp.net-core

如果我在我的模型中使用以下字段:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public DateTime RowVersion { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后将列定义为

`RowVersion` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Run Code Online (Sandbox Code Playgroud)

我从EF获得了正确的乐观并发行为.也就是说,我对使用时间戳感到很兴奋,因为它似乎只是第二个分辨率.虽然有2个客户尝试在1秒内更新同一记录的可能性很大,但肯定会发生,不是吗?

因此,考虑到这一点,我更喜欢一个简单的整数,在每次更新时以原子方式递增1.这样就不可能错过冲突.我把我的定义改为:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public long RowVersion { get; set; }
Run Code Online (Sandbox Code Playgroud)

问题是,MySQL不会自动增加它.所以我创建了一个触发器:

CREATE TRIGGER update_row_version BEFORE UPDATE on client 
FOR EACH ROW
SET NEW.RowVersion = OLD.RowVersion + 1;
Run Code Online (Sandbox Code Playgroud)

现在这一切都有效.EF在需要时抛出DbUpdateConcurrencyException,并且由于时间窗口而不可能错过更新.但是,它使用触发器,我继续阅读它们对性能有多糟糕.

那么还有更好的方法吗?也许某种方法来覆盖DbContext的SaveChanges()来在客户端上执行RowVersion增量,因此只对DB进行一次更新(我假设触发器实际上每次都进行两次更新)?

Jef*_*eff 6

好吧,我想出了一个似乎运行良好的策略,不需要触发器.

我添加了一个简单的界面:

interface ISavingChanges
{
    void OnSavingChanges();
}
Run Code Online (Sandbox Code Playgroud)

该模型现在看起来像这样:

public class Client : ISavingChanges
{
    // other fields omitted for clarity...


    [ConcurrencyCheck]
    public long RowVersion { get; set; }

    public void OnSavingChanges()
    {
        RowVersion++;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我像这样覆盖了SaveChanges:

    public override int SaveChanges()
    {
        foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified))
        {
            var saveEntity = entity.Entity as ISavingChanges;
            saveEntity.OnSavingChanges();
        }

        return base.SaveChanges();
    }
Run Code Online (Sandbox Code Playgroud)

这一切都按预期工作.ConcurrencyCheck属性是使EF在UPDATE SQL的SET和WHERE子句中包含RowVersion字段的关键.

  • 在使用Pomelo.EntityFrameworkCore.MySql驱动程序和ASP.NET核心应用程序时,我不得不使用此示例.虽然我更喜欢使用Fluent API来进行这样的配置.与MS文档相比,我不得不稍微修改它[这里](https://docs.microsoft.com/en-us/ef/core/modeling/concurrency)modelBuilder.Entity <EFEntity>() .Property(p => p.RowVersion).HasDefaultValue(0).IsConcurrencyToken(); (2认同)