無需修改實體和配置,在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
6.准備測試代碼
public static void Test() { var db1 = GetContext(); var customer1 = db1.Set<Customer>().FirstOrDefault(); customer1.PhoneNumber="t1"; using (var db2 = GetContext()) { var customer2 = db2.Set<Customer>().FirstOrDefault(); customer2.PhoneNumber = "t2"; db2.SaveChanges(); } db1.SaveChanges(); }
7.查看測試結果:
總結:
1.需要唯一版本號的生成支持,Sql Server(Compact)本身支持,MySql的uuid函數也支持。
2.需要設置Insert時的RowVersion默認值和更新RowVersion版本號,Sql Server(Compact)本身支持,MySql只支持不能用於RowVersion的TimeStamp的默認值和自動更新。因此在MySql中只能在應用中設置Row Version。
這個方案同時適用各種數據庫,尤其是類似MySql和Sqlite這種不支持默認RowVersion字段的數據庫。希望你不是找了好久才找到這個解決方案。