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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)

發(fā)布時(shí)間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一. 什么是事務(wù)

?  我們通俗的理解事務(wù)就是一系列操作要么全部成功、要么全部失敗(不可能存在部分成功,部分失敗的情況)。

?  舉一個(gè)事務(wù)在我們?nèi)粘I钪械慕?jīng)典例子:兩張銀行卡(甲、乙),甲向乙轉(zhuǎn)錢,整個(gè)過程需要執(zhí)行兩個(gè)操作,甲賬戶錢減少,乙賬戶錢增加,這是轉(zhuǎn)賬成功的情況;轉(zhuǎn)賬失敗時(shí)候,二者均不執(zhí)行,甲乙賬戶錢都不變。

1. SQLServer中的事務(wù)

  SQLServer中的事務(wù)具體封裝方式有多種,但無論是哪種,都需要依賴于這三句代碼:begin transaction?、commit transaction、rollback transaction。 

  ?具體怎么使用,詳見數(shù)據(jù)庫事務(wù)章節(jié):去復(fù)習(xí)

2. EF中的事務(wù)

?  EF中的事務(wù)主要分為三類,分別是SaveChanges、DbContextTransaction、TransactionScope。

  ?需要掌握的是這三種事務(wù)各自的使用方式和適用場(chǎng)景。

二. SaveChanges

?  SaveChanges是EF中最常見的事務(wù)了,在前面章節(jié),多次提到,SaveChanges一次性將本地緩存中所有的狀態(tài)變化一次性提交到數(shù)據(jù)庫,這就是一個(gè)事務(wù),要么統(tǒng)一成功,要么統(tǒng)一回滾。

  ?應(yīng)用場(chǎng)景:該種事務(wù)模式在實(shí)際開發(fā)中非常常用,在處理模塊業(yè)務(wù)同時(shí)需要對(duì)一張表(或多張表)進(jìn)行一系列增刪改操作,但這些操作都是要么都成功,要么都失敗,這種情況下可以在流程的最后 調(diào)用SaveChanges,一次性保存本地屬性狀態(tài)的全部變化。

 ?該模式作用于一個(gè)DBContext,即一個(gè)數(shù)據(jù)庫的EF的上下文,不能控制多個(gè)數(shù)據(jù)庫。

?  ?比如:注冊(cè)場(chǎng)景,可能需要向用戶表、日志表、賬戶表等多張表中插入數(shù)據(jù),其中一張表插入失敗,所有的都需要回滾,這種情況,就可以在該業(yè)務(wù)的最后,統(tǒng)一調(diào)用SaveChanges一次性的事務(wù)提交。

1. 事務(wù)成功

1 private static void TestSaveChanges()2 {3 using (DbContext db = new CodeFirstModel())4 {5 //增加6 TestInfor t1 = new TestInfor()7 {8 id = Guid.NewGuid().ToString("N"),9 txt1 = "txt1", 10 txt2 = "txt2" 11 }; 12 db.Set<TestInfor>().Add(t1); 13 //刪除 14 TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault(); 15 if (t2 != null) 16 { 17 db.Set<TestInfor>().Remove(t2); 18 } 19 //修改 20 TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault(); 21 t3.txt2 = "我是李馬茹23"; 22 23 //SaveChanges事務(wù)提交 24 int n = db.SaveChanges(); 25 Console.WriteLine("數(shù)據(jù)作用條數(shù):" + n); 26 } 27 }

2. 事務(wù)回滾

1 private static void TestSaveChangesFailure()2 {3 using (DbContext db = new CodeFirstModel())4 {5 //增加6 TestInfor t1 = new TestInfor()7 {8 id = Guid.NewGuid().ToString("N"),9 txt1 = "txt1", 10 txt2 = "txt2" 11 }; 12 db.Set<TestInfor>().Add(t1); 13 //刪除 14 TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault(); 15 if (t2 != null) 16 { 17 db.Set<TestInfor>().Remove(t2); 18 } 19 //修改 20 TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault(); 21 t3.txt2 = "我是李馬茹23"; 22 //增加(制造錯(cuò)誤) 23 TestInfor t4 = new TestInfor() 24 { 25 id = Guid.NewGuid().ToString("N") + "123", 26 txt1 = "txt1", 27 txt2 = "txt2" 28 }; 29 db.Set<TestInfor>().Add(t4); 30 31 //SaveChanges事務(wù)提交 32 int n = db.SaveChanges(); 33 Console.WriteLine("數(shù)據(jù)作用條數(shù):" + n); 34 } 35 }

三. DbContextTransaction事務(wù)

  該事務(wù)為EF6新增的事務(wù),通常用于手動(dòng)接管事務(wù),某些操作是一個(gè)事務(wù),某些操作是另外一個(gè)事務(wù)。

  使用場(chǎng)景:EF調(diào)用SQL語句的時(shí)候使用該事務(wù)、 多個(gè)SaveChanges的情況(解決自增ID另一個(gè)業(yè)務(wù)中使用的場(chǎng)景)。

  核心代碼:BeginTransaction、Commit、Rollback、Dispose. 如果放到using塊中,就不需要手動(dòng)Dispose了。

  該種事務(wù)與數(shù)據(jù)庫中的transaction事務(wù)原理一致,在EF中,由于每次調(diào)用 db.Database.ExecuteSqlCommand(sql1, pars1)的時(shí)候,即刻執(zhí)行了該SQL語句,所以要把他放到一個(gè)大的事務(wù)中,整體提交、回滾.(EF6新增的)

1. 事務(wù)成功

1 private static void TestDbContextTransaction()2 {3 using (DbContext db = new CodeFirstModel())4 {5 DbContextTransaction trans = null;6 try7 {8 //開啟事務(wù)9 trans = db.Database.BeginTransaction(); 10 //增加 11 string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)"; 12 SqlParameter[] pars1 ={ 13 new SqlParameter("@id",Guid.NewGuid().ToString("N")), 14 new SqlParameter("@txt1","txt11"), 15 new SqlParameter("@txt2","txt22") 16 }; 17 db.Database.ExecuteSqlCommand(sql1, pars1); 18 //刪除 19 string sql2 = @"delete from TestInfor where id=@id"; 20 SqlParameter[] pars2 ={ 21 new SqlParameter("@id","22") 22 }; 23 db.Database.ExecuteSqlCommand(sql2, pars2); 24 //修改 25 string sql3 = @"update TestInfor set txt1=@txt1 where id=@id"; 26 SqlParameter[] pars3 ={ 27 new SqlParameter("@id","3"), 28 new SqlParameter("@txt1","二狗子") 29 }; 30 db.Database.ExecuteSqlCommand(sql3, pars3); 31 32 //提交事務(wù) 33 trans.Commit(); 34 Console.WriteLine("事務(wù)成功了"); 35 } 36 catch (Exception ex) 37 { 38 Console.WriteLine(ex.Message); 39 trans.Rollback(); 40 41 } 42 finally 43 { 44 trans.Dispose(); //也可以把該事務(wù)寫到using塊中,讓其自己托管,就不需要手動(dòng)釋放了 45 } 46 } 47 }

2. 事務(wù)回滾

1 private static void TestDbContextTransactionFailure()2 {3 using (DbContext db = new CodeFirstModel())4 {5 DbContextTransaction trans = null;6 try7 {8 //開啟事務(wù)9 trans = db.Database.BeginTransaction(); 10 //增加 11 string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)"; 12 SqlParameter[] pars1 ={ 13 new SqlParameter("@id",Guid.NewGuid().ToString("N")), 14 new SqlParameter("@txt1","txt11"), 15 new SqlParameter("@txt2","txt22") 16 }; 17 db.Database.ExecuteSqlCommand(sql1, pars1); 18 //刪除 19 string sql2 = @"delete from TestInfor where id=@id"; 20 SqlParameter[] pars2 ={ 21 new SqlParameter("@id","22") 22 }; 23 db.Database.ExecuteSqlCommand(sql2, pars2); 24 //修改 25 string sql3 = @"update TestInfor set txt1=@txt1 where id=@id"; 26 SqlParameter[] pars3 ={ 27 new SqlParameter("@id","3"), 28 new SqlParameter("@txt1","二狗子222") 29 }; 30 db.Database.ExecuteSqlCommand(sql3, pars3); 31 32 33 //增加(制造錯(cuò)誤) 34 string sql4 = @"insert into TestInfor values(@id,@txt1,@txt2)"; 35 SqlParameter[] pars4 ={ 36 new SqlParameter("@id",Guid.NewGuid().ToString("N")+"123"), 37 new SqlParameter("@txt1","txt11"), 38 new SqlParameter("@txt2","txt22") 39 }; 40 db.Database.ExecuteSqlCommand(sql4, pars4); 41 42 //提交事務(wù) 43 trans.Commit(); 44 Console.WriteLine("事務(wù)成功了"); 45 } 46 catch (Exception ex) 47 { 48 Console.WriteLine(ex.Message); 49 trans.Rollback(); 50 51 } 52 finally 53 { 54 trans.Dispose(); 55 } 56 } 57 }

3. 自DbContextTransaction事務(wù)測(cè)試多個(gè)SaveChanges的情況統(tǒng)一回滾

1 private static void DbContextTransSaveChanges()2 {3 using (DbContext db = new CodeFirstModel())4 {5 //自動(dòng)脫管,不需要手動(dòng)釋放6 using (DbContextTransaction trans = db.Database.BeginTransaction())7 {8 try9 { 10 TestInfor t1 = new TestInfor() 11 { 12 id = Guid.NewGuid().ToString("N"), 13 txt1 = "111111111", 14 txt2 = "222222222222" 15 }; 16 db.Entry(t1).State = EntityState.Added; 17 db.SaveChanges(); 18 19 //人為制造失敗情況 20 TestInfor t2 = new TestInfor() 21 { 22 id = Guid.NewGuid().ToString("N") + "123", 23 txt1 = "111111111", 24 txt2 = "222222222222" 25 }; 26 db.Entry(t2).State = EntityState.Added; 27 db.SaveChanges(); 28 29 trans.Commit(); 30 } 31 catch (Exception) 32 { 33 trans.Rollback(); 34 } 35 36 } 37 } 38 }

四. TransactionScope事務(wù)

1. 該事務(wù)用來處理多個(gè)SaveChanges的事務(wù)(特殊情況的業(yè)務(wù))或者多個(gè)DBContext(每個(gè)DBContext是一個(gè)實(shí)例,代表不同的數(shù)據(jù)庫連接).

2. 核心代碼:(一個(gè)Complete函數(shù)走天下,異常的話,自動(dòng)回滾 ,也可以結(jié)合try-catch Transaction.Current.Rollback();實(shí)現(xiàn)回滾)

       ??需要引入程序集:using System.Transactions;

3. 適用場(chǎng)景:

  ①該種事務(wù)適用于多數(shù)據(jù)庫連接的情況

  特別注意:如果使用該事務(wù)來處理多個(gè)數(shù)據(jù)庫(多個(gè)DBContex)時(shí),必須手動(dòng)開啟msdtc服務(wù),這樣才可以將多個(gè)DB的SaveChange給放到一個(gè)事務(wù)中,如果失敗,?則多個(gè)數(shù)據(jù)庫的數(shù)據(jù)統(tǒng)一回滾.

?  ??開啟msdtc服務(wù)的步驟: cmd命令→net start msdtc

?  ?②主鍵id自增的情況,同一個(gè)業(yè)務(wù)線中,需要拿到新增加的數(shù)據(jù)的主鍵id,進(jìn)行操作。

? ? ? ③多線程帶鎖的情況,同一條業(yè)務(wù)線前半部分必須先SaveChanges,才能保證數(shù)據(jù)準(zhǔn)確性(測(cè)試簡單版本,實(shí)際的業(yè)務(wù)場(chǎng)景待補(bǔ)充!!!)

4. 主鍵自增被使用的情況的解決方案

1     private static void Test3()2 {3 using (DbContext db = new CodeFirstModel())4 {5 using (TransactionScope trans = new TransactionScope())6 {7 try8 {9 TestInfor2 t1 = new TestInfor2() 10 { 11 txt11 = Guid.NewGuid().ToString("N"), 12 txt22 = Guid.NewGuid().ToString("N") 13 }; 14 db.Set<TestInfor2>().Add(t1); 15 //如果這里不寫SaveChanges的話,t1.id永遠(yuǎn)為0 16 db.SaveChanges(); 17 18 19 TestInfor2 t2 = new TestInfor2() 20 { 21 txt11 = (t1.id + 1).ToString(), 22 txt22 = Guid.NewGuid().ToString("N") 23 }; 24 db.Entry(t2).State = EntityState.Added; 25 db.SaveChanges(); 26 27 //事務(wù)統(tǒng)一提交(若失敗則統(tǒng)一回滾) 28 trans.Complete(); 29 Console.WriteLine("自增提交成功"); 30 } 31 catch (Exception) 32 { 33 Transaction.Current.Rollback(); 34 } 35 } 36 } 37 }

5. 多個(gè)DBContext、多個(gè)數(shù)據(jù)庫的情況。

1 private static void TransactionScopeTwoDB()2 {3 using (TransactionScope trans = new TransactionScope())4 {5 try6 {7 DbContext db1 = new CodeFirstModel();8 DbContext db2 = new CodeFirstModel2();9 //數(shù)據(jù)庫1 10 TestInfor t1 = new TestInfor() 11 { 12 id = Guid.NewGuid().ToString("N"), 13 txt1 = "111111111", 14 txt2 = "222222222222" 15 }; 16 db1.Entry(t1).State = EntityState.Added; 17 db1.SaveChanges(); 18 19 //數(shù)據(jù)庫2 20 TestOne t2 = new TestOne() 21 { 22 id = Guid.NewGuid().ToString("N"), //可以在此處手動(dòng)制造個(gè)錯(cuò)誤,來測(cè)試多數(shù)據(jù)庫的回滾問題 23 t1 = "222", 24 t2 = "2222" 25 }; 26 db2.Entry(t2).State = EntityState.Added; 27 db2.SaveChanges(); 28 29 trans.Complete(); 30 Console.WriteLine("多個(gè)數(shù)據(jù)庫添加成功"); 31 } 32 catch (Exception) 33 { 34 Transaction.Current.Rollback(); 35 } 36 37 } 38 }

總結(jié)

以上是生活随笔為你收集整理的第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。