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

我目前正在使用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 。

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

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

大警告: 没有测试 – 只是大声思考。

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启用更改validation,并且应该提供服务。

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

这是经过测试的解决方案(适用于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() .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 。 它对我来说很完美。