EntityFramework系列:MySql的RowVersion

无需修改实体和配置,在MySql中使用和SqlServer一致的并发控制。修改RowVersion类型不可取,修改为Timestamp更不可行。Sql Server的RowVersion生成一串唯一的二进制保证Row的版本,无关TimeStamp,更无论TimeStamp的精度问题。使用MySql触发器只能解决uuid的插入的默认值和更新的随机值,由于MySql的自身为了防止无限递归的策略,它的触发器无法在当前表的触发器中更新当前表,所以触发器无法实现更新在SqlServer中由数据库生成的RowVersion字段的值。所以MySql中的RowVersion只能由应用程序赋值。
在EF中采用IsConcurrencyToken配置后RowVersion即自动用于where子句中用于比较Row Version,通过重写SaveChanges方法在每次添加和更新时设置RowVersion的值即可实现在更新时同时比较Row Version的当前版本和更新Row Version的目的,同时可以正确的取回更新后的Row Version值。
1.定义并发控制字段
public interface IRowVersion
byte[] RowVersion { get; set; }
}
2.配置并发控制字段
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.AddFromAssembly(typeof(MySqlDbContext).Assembly);
modelBuilder.Properties().Where(o => typeof(IRowVersion).IsAssignableFrom(o.DeclaringType)&&o.PropertyType==typeof(byte[])&&o.Name=="RowVersion")
.Configure(o => o.IsConcurrencyToken().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None));
Database.SetInitializer(new MySqlDbInitializer());
}
3.手动对RowVersion赋值
public override int SaveChanges()
this.ChangeTracker.DetectChanges();
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
foreach (ObjectStateEntry entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added))
var v = entry.Entity as IRowVersion;
if (v != null)
v.RowVersion = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
return base.SaveChanges();
}
4.检查生成的Sql语句
UPDATE `Customer` SET `PhoneNumber`=@gp1, `RowVersion`=@gp2 WHERE (`Id` = 1) AND (`RowVersion` = @gp3)
-- @gp1: '635655975120384389' (Type = String, IsNullable = false, Size = 18)
-- @gp2: 'System.Byte[]' (Type = Object, IsNullable = false, Size = 36)
-- @gp3: 'System.Byte[]' (Type = Object, IsNullable = false, Size = 36)
5.查看数据中的RowVersion

image
6.准备测试代码
public static void Test()
var db1 = GetContext();
var customer1 = db1.Set<Customer>().FirstOrDefault();
customer1.PhoneNumber="t1";
using (var db2 = GetContext())