EntityFramework系列:MySql的RowVersion

剛哥521發表於2015-04-25

無需修改實體和配置,在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欄位的資料庫。希望你不是找了好久才找到這個解決方案。

相關文章