日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

EntityFramework进阶——数据变更冲突

發布時間:2025/3/11 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EntityFramework进阶——数据变更冲突 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TimeStamp

更新操作可能伴隨數據沖突,我們可以通過并發處理妥善解決這一方面的問題。避免數據沖突比較方便的做法是自動加入字節數組(byte[])類型的TimeStamp屬性,對應到數據表中的rowvewsion類型字段,自動監控數據的更新操作。

下面通過一個例子來說明:

新建一個項目,名稱為TimeStampDemo,新增實體類如下圖所示:

public class Product{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }public int Quantity { get; set; }1741660715public byte[] Timestamp { get; set; }}

上下文代碼如下圖所示:

public class TimeStampModel : DbContext{public TimeStampModel(): base("name=TimeStampModel"){}public virtual DbSet<Product> Product { get; set; }}

首次運行項目,在數據庫中生成了如下表結構:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

下面編寫程序來查看其中的Timestamp字段值:

static void Main(string[] args) {using (TimeStampModel model = new TimeStampModel()){Product product = model.Product.Where(p => p.Id == 1).First();long ts = BitConverter.ToInt64(product.Timestamp,0);Console.WriteLine("Quantity:{0} \t Timestamp:{1}",product.Quantity,ts);product.Quantity = 150;model.SaveChanges();ts = BitConverter.ToInt64(product.Timestamp,0);Console.WriteLine("Quantity:{0} \t Timestamp:{1}",product.Quantity,ts);Console.ReadKey();} }

運行效果下圖所示:

? ? ? ? ? ? ? ? ?

可以看到,每一次更新數據時,EF都會修改Timestamp的值。如果期間有其他程序搶先完成更新造成數據變更,那么EF就會報錯。

我們重新執行程序,在首次出現Timestamp語句時,打上斷點。然后在SQL查詢界面直接更新用SQL語句更新數據:

UPDATE dbo.Products SET Quantity = 200 WHERE Id = 1

SQL執行之前Timestamp的值:?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

SQL執行之后Timestamp的值:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

可以看到每次更新數據后,Timestamp的數值會發生改變, 這時繼續運行程序,就會報錯:

?

此信息描述找不到要更新的數據,這是因為EF在更新每一項數據時,除了主鍵之外,還會對比當時取出的TimeStamp的值。由于我們使用外部SQL更新,EF無法檢測這個字段就出現報錯。

異常DbUpdateConcurrencyException派生自DbUpdateException,表示一個并發更新沖突,當這個異常出現時,顯示SaveChanges更新失敗,通過DbUpdateException.Entries屬性可以獲取其返回的IEnumerable<DbEntityEntry>對象,其中存取未成功更新的實體數據對象。?

重新調整Main方法中的代碼如下圖所示:

static void Main(string[] args) {using (TimeStampModel model = new TimeStampModel()){Product product = model.Product.Where(p => p.Id == 1).First();long ts = BitConverter.ToInt64(product.Timestamp, 0);Console.WriteLine("Quantity:{0} \t TimeStamp:{1}", product.Quantity, ts);try{Console.WriteLine("新的 Quantity 值:");int quantity = 500;product.Quantity = quantity;model.SaveChanges();ts = BitConverter.ToInt64(product.Timestamp, 0);Console.WriteLine("Quantity:{0} \t TimeStamp:{1}", product.Quantity, ts);}catch (DbUpdateConcurrencyException ex){DbEntityEntry entry = ex.Entries.Single();DbPropertyValues current = entry.CurrentValues;//當前實體數據int quantity = current.GetValue<int>("Quantity");long timestamp = BitConverter.ToInt64(current.GetValue<byte[]>("Timestamp"),0);DbPropertyValues dbvalue = entry.GetDatabaseValues();//數據庫中的實體數據int dbquantity = dbvalue.GetValue<int>("Quantity");long dbtimestamp = BitConverter.ToInt64(dbvalue.GetValue<byte[]>("Timestamp"),0);Console.WriteLine("DbUpdateConcurrentException.....");Console.WriteLine("當前實體的屬性值: Quantity:{0} \t TimeStamp:{1}",quantity,timestamp);Console.WriteLine("數據庫中的字段值:Quantity:{0} \t TimeStamp:{1}",dbquantity,dbtimestamp);}} }

再次在首次出現Timestamp語句時,打上斷點。然后在SQL查詢界面直接更新用SQL語句更新數據后,繼續運行程序,如下圖所示:

? ? ? ? ? ? ? ?

?


并發沖突處理——Database Wins 或者 Client Wins?

處理更新沖突有幾種方式,想以數據庫中的值來覆蓋當前數據對象的值,則可以調用DbEntityEntry類定義的Reload方法,此方法執行的是Database Wins(數據庫優先)策略,執行完畢之后,數據對象當前的值將與數據庫同步。

entry.Relaod(); entry.SaveChanges();

另一種方式則是反向以當前的值覆蓋數據庫的值,稱為Clinet? Wins(客戶優先)策略。

entry.OriginalValues.SetValues(entry.GetDatabaseValues()); model.SaveChanges()

?

添加一個ResolveConcurrency方法來支持并發沖突處理:

public static void ResolveConcurrency(TimeStampModel model, DbEntityEntry entry) {Console.WriteLine("1.Database wins 2.Client wins:");int i = int.Parse(Console.ReadLine());if (i == 1){entry.Reload();model.SaveChanges();Console.WriteLine("與數據庫同步完成——DataBase Wins");}else{entry.OriginalValues.SetValues(entry.GetDatabaseValues());model.SaveChanges();Console.WriteLine("重新更新數據庫完成——Client Wins");} }

ResolveConcurrency方法插入上述例子的catch語句中,并在并發沖突中選擇與數據庫同步完成,結果如下圖所示:

? ? ? ? ? ? ? ? ? ? ? ? ??

?


ConcurrencyCheck注解?

TimeStamp注解屬性會導致EF在更新時監控整項數據的更新狀態,如果對特定字段進行數據沖突的監控,則可以通過ConcurrencyCheck注解來達到目的。

下面通過程序來說明,新建一個控制臺應用程序ConcurrencyCheckDemo,有如下一個實體類:

public class Product{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }[ConcurrencyCheck]public int SPrice { get; set; }}

在特價字段(SPrice)設置【ConcurrencyCheck】,當程序更新任何一項Product數據時,SPrice字段將受到監控,期間不受EF監控的程序變更了SPrice字段的值時,便會產生沖突。其他字段沒有設置,因此數據的更新不會有沖突。

在Main函數中增加如下圖代碼:

static void Main(string[] args) {using (ConcurrencyCheckModel db = new ConcurrencyCheckModel()){try{Console.WriteLine("指定更新操作:A.商品名稱 B.商品特價");string ab = Console.ReadLine();if (ab == "A"){Console.WriteLine("輸入第一項商品的新名稱:");string name = Console.ReadLine();db.Product.First().Name = name;}if (ab == "B"){Console.WriteLine("輸入第一項商品的新特價:");int price = int.Parse(Console.ReadLine());db.Product.First().SPrice = price;}Console.WriteLine("按任意鍵完成更熱");Console.ReadKey();db.SaveChanges();Console.WriteLine("完成更新");}catch (Exception ex){Console.WriteLine(ex.ToString());Console.WriteLine(ex.Message);}} }

當修改商品特價時,去數據庫修改SPrice字段的值,便會報錯:

? ? ? ?

?


?

?

?

?

?

?

總結

以上是生活随笔為你收集整理的EntityFramework进阶——数据变更冲突的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。