TimeStamp/RowVersion
前言
通常資料在update的時候,有兩個人同時間把同一筆資料查詢出來,接下來修改完後存改,如果沒有經過一些特別處理,容易發生一個已經修改完的資料被另一個人覆蓋掉的情形。 而使用Entity Framework可以很簡單的使用Timestamp解決這種平行處理的問題。
註:Timestamp的欄位作用是,只要該筆的資料被異動過,則Timestamp會產生一筆新的值,藉由這種方式,來達到安全平行處理的方式。
Timestamp
Convention | None |
Data Annotation | Timestamp |
Fluent | Entity<T>.Property(t => t.PropertyName).IsRowVersion() |
[Timestamp]
public byte[] RwoVersion { get; set; }
只要在Domain Class加上一個有Timestamp attribute的Property,在資料庫就會產生一個對應data type 為 Timestamp的欄位
有了Timestamp欄位後,當使用Entity Framework更新資料時,所產生的Update Script會多一個RowVersion判斷條件。
exec sp_executesql N'UPDATE [dbo].[People]
SET [FirstName] = @0
WHERE (([SocialNumber] = @1) AND ([RowVersion] = @2))
SELECT [RwoVersion]
FROM [dbo].[People]
WHERE @@ROWCOUNT > 0 AND [SocialNumber] = @1',N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'Bob',@1=88,@2=0x0000000000001774
用這種方式,就可以確保更新此筆資料的時候,資料是還沒有被異動過的。 若不幸在更新的時候已經被異動過了,則RowVersion的值會不一致,造成Update失敗的問題,此時Entity Framework會拋出DbUpdateConcurrencyException,防止造成資料覆蓋的問題。
備註:RowVersion 和 TimeStamp指的是同一種data type,SQL Server 通常使用TimeStamp命名,其他資料庫則是使用RowVersion命名。
Non-Timestamp
Convention | None |
Data Annotation | ConcurrencyCheck |
Fluent | Entity<T>.Property(t=>t.PropertyName).IsConcurrencyToken() |
有些資料庫如果沒有支援Timestamp,其實也是可以做到部分的平行處理功能。 使用ConcurrencyCheck指定某一個欄位,則Entity Framework產生的Update Script會包含蓋欄位的條件。 使用ConcurrencyCheck的欄位沒有Timestamp每次Update後數值都會不一樣的特性就是了。
如以下範例:
public class Person
{
[Key]
public int PersonId { get; set; }
[ConcurrencyCheck]
public int SocialNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime CreatedDate { get; set; }
}
產生的Update Script如下
exec sp_executesql N'UPDATE [dbo].[People]
SET [FirstName] = @0
WHERE (([PersonId] = @1) AND ([SocialNumber] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 int',@0=N'Bob',@1=1,@2=1234
這樣就可以保證兩個人以後同時update的時候,改到SocialNumber造成資料覆蓋的問題,只是這個效果沒有Timestampe那麼好就對了,因為SocialNumber是沒辦法自動變成其他數值。 而如果同時Update時,不幸別人改到SocialNumber造成Update失敗,則一樣會拋出DbUpdateConcurrencyException。
一天一分享,身體好健康。
該追究的不是過去的原因,而是現在的目的。