使用Entity Framework和MySQL实现乐观并发

时间:2011-09-30 09:44:44

标签: c# .net mysql entity-framework concurrency

我目前正在使用Entity Framework 4.1和MySQL开发应用程序。我想使用乐观并发,因此需要创建一个表结构,允许EF检测并发问题。我的目标与此类似:http://blogs.msdn.com/b/alexj/archive/2009/05/20/tip-19-how-to-use-optimistic-concurrency-in-the-entity-framework.aspx

我的问题是,与MS SQL Server相比,MySQL中的时间戳类型不同。除此之外,时间戳和日期时间都没有提供MySQL的亚秒级精度(http://feedblog.org/2007/05/26/why-doesnt-mysql-support-millisecond-datetime-resolution/)。因此,这些类型在检测并发性问题时非常糟糕。

我可以用什么其他数据类型来解决这个问题?我在考虑使用Guid。但是这种方法存在两个潜在的问题:1。MySQL将Guids存储为char(36),使得效率非常低。 2.我不确定EF是否要求行版本严格增加,或者它是否足够独特。

3 个答案:

答案 0 :(得分:3)

重要警告: 未经测试 - 只是大声思考。

EF支持覆盖SaveChanges,因此可能有一个选项是定义一个接口,例如:

interface IVersionedRow {
    int RowVersion {get;set;}
}

并在模型类和数据库表中添加int RowVersion属性/字段,并使用partial class实现此接口(使用隐式接口实现):

partial class Customer : IVersionedRow {}
partial class Order : IVersionedRow {}
...

然后覆盖SaveChanges,例如:

public override int SaveChanges(SaveOptions options)
{    
    foreach (ObjectStateEntry entry in
        ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
    {
        var v = entry.Entity as IVersionedRow;
        if(v != null) v.RowVersion++;
    }
    return base.SaveChanges(options);
}

然后应该作为手动实现的行版本计数器运行(理论上 - 未经测试)。保留为RowVersion启用更改验证,并且应该提供。

答案 1 :(得分:1)

我刚刚提交了PR to MySQL .NET Connector v6.9.10,为此问题提供了解决方案,可在EF和非EF应用程序之间提供乐观锁定。有关详细信息,请参阅https://stackoverflow.com/a/50147396/365261

答案 2 :(得分:0)

这是经过测试的解决方案(适用于EF6及以上版本)。

您需要在模型中执行以下操作:

    [Table("some_table")]
    public class SomeEntity : IVersionedRow //define an interface as described previously and replace int type to long
    {
        ...
        [Column("row_version")]
        public long RowVersion { get; set; }
    }

然后在你的背景下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<SomeEntity>()
        .Property(p => p.RowVersion).IsConcurrencyToken();
    base.OnModelCreating(modelBuilder);
}

public override int SaveChanges()
{    
    var objectContextAdapter = this as IObjectContextAdapter;
    if (objectContextAdapter != null) {
        objectContextAdapter.ObjectContext.DetectChanges();
        foreach (ObjectStateEntry entry in objectContextAdapter.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)) {
            var v = entry.Entity as IVersionedRow;
            if (v != null) 
                v.RowVersion++;
        }
    }
    return base.SaveChanges();
}

现在,当您执行更新或删除操作时,请不要忘记包含DbUpdateConcurrencyException。它对我来说很完美。