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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

了解Entity Framework中事务处理

發布時間:2023/12/10 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 了解Entity Framework中事务处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ??? Entity Framework 6以前,框架本身并沒有提供顯式的事務處理方案,在EF6中提供了事務處理的API。

????? 所有版本的EF,只要你調用SaveChanges方法進行插入、修改或刪除,EF框架會自動將該操作進行事務包裝。這種方法無法對事務進行顯式的控制,例如新建事務等,可能會造成事務的粒度非常大,降低效率。EF不會對查詢進行事務包裝。

???? 從EF6開始,默認情況下,如果每次調用Database.ExecuteSqlCommand(),如果其不在存在于任何事務中,則會將該Command包裝到一個事務中。框架提供了多種重載,允許你重寫這些方法,實現事務的控制。同樣,執行存儲過程的ObjectContext.ExecuteFunction()方法是實現了這種機制(但是ExecuteFunction不能被重寫)。這兩種情況下,使用的事務隔離級別均為數據庫提供的默認隔離級別,SQL Server中使用的是READ COMMITED。

????? 有同學提供了EF6之前版本的事務方案,如下:

1 using (BlogDbContext context =new BlogDbContext())2 {3 using (TransactionScope transaction =new TransactionScope())4 {5 context.BlogPosts.Add(blogPost);6 context.SaveChanges();7 postBody.ID = blogPost.ID;8 context.EntryViewCounts.Add(9 new EntryViewCount() { EntryID = blogPost.ID }); 10 context.PostBodys.Add(postBody); 11 context.SaveChanges(); 12 //提交事務 13 transaction.Complete(); 14 } 15 }

????? 其實,上面方法執行結果不會錯,但是存在隱患,這樣情況下,顯式事務其實是多余的。所以我對這種方案持懷疑態度(沒有進行內部代碼的分析,有時間了分析下,希望大家拍磚)。

????? 官方體統的解決方案為:

1 using System.Collections.Generic; 2 using System.Data.Entity; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Transactions; 6 7 namespace TransactionsExamples 8 { 9 class TransactionsExample 10 { 11 static void UsingTransactionScope() 12 { 13 using (var scope = new TransactionScope(TransactionScopeOption.Required)) 14 { 15 using (var conn = new SqlConnection("...")) 16 { 17 conn.Open(); 18 19 var sqlCommand = new SqlCommand(); 20 sqlCommand.Connection = conn; 21 sqlCommand.CommandText = 22 @"UPDATE Blogs SET Rating = 5" + 23 " WHERE Name LIKE '%Entity Framework%'"; 24 sqlCommand.ExecuteNonQuery(); 25 26 using (var context = 27 new BloggingContext(conn, contextOwnsConnection: false)) 28 { 29 var query = context.Posts.Where(p => p.Blog.Rating > 5); 30 foreach (var post in query) 31 { 32 post.Title += "[Cool Blog]"; 33 } 34 context.SaveChanges(); 35 } 36 } 37 38 scope.Complete(); 39 } 40 } 41 } 42 }

?

????? 一般情況下,用戶不需要對事務進行特殊的控制,使用EF框架默認行為即可。如果要對細節進行控制,參考下面章節:

EF6 API工作機制

EF6以前版本EF框架自己管理數據庫連接,如果你自己嘗試打開連接可能會拋出異常(打開一個已打開的連接會拋出異常)。由于事務必須在一個打開的連接上執行,因此要合并一系列操作到一個事務中,要么使用TractionScope,要么使用ObjectContext.Connection屬性直接執行EntityConnection的Open(),并BeginTransaction()。另外,如果你在數據庫底層連接上執行了事務,上面API會失敗。

注意:EF6中移除了僅接受關閉連接的限制。

EF6 開始提供了:

Database.BeginTransaction() : 為用戶提供一種簡單易用的方案,在DbContext中啟動并完成一個事務 -- 合并一系列操作到該事務中。同時使用戶更方便的指定事務隔離級別。

Database.UseTransaction() : 允許DbContext使用一個EF框架外的事務。

在同一DbContext中合并一系列操作到一個事務中

Database.BeginTransaction()有兩個重載方法。一個方法提供一個IsolationLevel參數,另一個無參方法使用底層數據庫提供程序默認的數據庫事務隔離級別。兩個重載方法均返回一個DbContextTransaction對象,該對象提供Commit和Rollback方法,用于數據庫底層事務的提交和回滾。

使用DbContextTransaction意味著,一旦提交或回滾事務,就要釋放該對象。一種簡單的方法是使用using語法,在using代碼塊結束時自動調用該對象的Dispose方法。

1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.SqlClient; 5 using System.Linq; 6 using System.Transactions; 7 8 namespace TransactionsExamples 9 { 10 class TransactionsExample 11 { 12 static void StartOwnTransactionWithinContext() 13 { 14 using (var context = new BloggingContext()) 15 { 16 using (var dbContextTransaction = context.Database.BeginTransaction()) 17 { 18 try 19 { 20 context.Database.ExecuteSqlCommand( 21 @"UPDATE Blogs SET Rating = 5" + 22 " WHERE Name LIKE '%Entity Framework%'" 23 ); 24 25 var query = context.Posts.Where(p => p.Blog.Rating >= 5); 26 foreach (var post in query) 27 { 28 post.Title += "[Cool Blog]"; 29 } 30 31 context.SaveChanges(); 32 33 dbContextTransaction.Commit(); 34 } 35 catch (Exception) 36 { 37 dbContextTransaction.Rollback(); 38 } 39 } 40 } 41 } 42 } 43 }

注意:啟動一個事務需要底層數據庫連接已打開。因此,如果連接未打開,調用Database.BeginTransaction()會打開連接,在其Dispose時關閉連接。

傳遞一個現有事務到DbContext

????? 有時,你可能需要在同一數據庫上,執行一個EF框架之外更大范圍的事務,這是就需要自己打開連接并啟動事務,然后通知EF框架:

1) 使用已打開的數據庫連接

2) 在該連接上使用現有的事務

????? 要實現上面的行為,你需要使用繼承自DbContext的構造方法XXXContext(conn,contextOwnsConnection),其中:

?????????????????? conn : 是一個已存在的數據庫連接

???????????????????contextOwnsConnection : 是一個布爾值,指示上下文是否自己占用數據庫連接。

注意:這種情況下,contextOwnsConnection必須設置為false,因為它通知EF框架,在自己使用完連接后,不要關閉它。見下面代碼:

1 using (var conn = new SqlConnection("...")) 2 { 3 conn.Open(); 4 using (var context = new BloggingContext(conn, contextOwnsConnection: false)) 5 { 6 } 7 }

????? 此外,你必須自己啟動事務(如果你不想使用默認IsolationLevel,可以自己設置之)并讓EF框架知道該連接上已經存在已啟動的事務(參考下面代碼的33行)。
????? 然后就可以直接在連接上執行數據庫操作,或者在DbContext上執行,所有這些操作均在同一事務中執行,你負責提交或回滾事務,并調用DatabaseTransaction.Dispose(),最后要關閉和釋放數據庫連接。請參考以下代碼:

1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.SqlClient; 5 using System.Linq; 6 sing System.Transactions; 7 8 namespace TransactionsExamples 9 { 10 class TransactionsExample 11 { 12 static void UsingExternalTransaction() 13 { 14 using (var conn = new SqlConnection("...")) 15 { 16 conn.Open(); 17 18 using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.Snapshot)) 19 { 20 try 21 { 22 var sqlCommand = new SqlCommand(); 23 sqlCommand.Connection = conn; 24 sqlCommand.Transaction = sqlTxn; 25 sqlCommand.CommandText = 26 @"UPDATE Blogs SET Rating = 5" + 27 " WHERE Name LIKE '%Entity Framework%'"; 28 sqlCommand.ExecuteNonQuery(); 29 30 using (var context = 31 new BloggingContext(conn, contextOwnsConnection: false)) 32 { 33 context.Database.UseTransaction(sqlTxn); 34 35 var query = context.Posts.Where(p => p.Blog.Rating >= 5); 36 foreach (var post in query) 37 { 38 post.Title += "[Cool Blog]"; 39 } 40 context.SaveChanges(); 41 } 42 43 sqlTxn.Commit(); 44 } 45 catch (Exception) 46 { 47 sqlTxn.Rollback(); 48 } 49 } 50 } 51 } 52 } 53 }

注意:

  • 你可以傳遞null到方法Database.UseTransaction()來清除EF框架對當前事務的記憶。如果你這樣做,事務既不會提交也不會回滾。所以要謹慎使用之,除非你確實需要這樣。
  • 如果EF框架已經持有一個事務,此時你傳遞一個事務,Database.UseTransaction()將拋出一個異常:

?????? ★ EF框架已經持有一個事務;

?????? ★ 當EF框架已經在一個TransactionScope中運行;

?????? ★ 其數據庫連接對象為null (例如,無連接--通常這種情況表示事務已經完成);

?????? ★ 數據庫連接對象與EF框架的數據庫連接對象不匹配;

?對TransactionScope的一些補充

如果你使用.net framework 4.5.1及以上版本,可以使用TransactionScope的TransactionScopeAsyncFlowOption參數提供對異步的支持:

1 using System.Collections.Generic; 2 using System.Data.Entity; 3 using System.Data.SqlClient; 4 using System.Linq; 5 using System.Transactions; 6 7 namespace TransactionsExamples 8 { 9 class TransactionsExample 10 { 11 public static void AsyncTransactionScope() 12 { 13 using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 14 { 15 using (var conn = new SqlConnection("...")) 16 { 17 await conn.OpenAsync(); 18 19 var sqlCommand = new SqlCommand(); 20 sqlCommand.Connection = conn; 21 sqlCommand.CommandText = 22 @"UPDATE Blogs SET Rating = 5" + 23 " WHERE Name LIKE '%Entity Framework%'"; 24 await sqlCommand.ExecuteNonQueryAsync(); 25 26 using (var context = new BloggingContext(conn, contextOwnsConnection: false)) 27 { 28 var query = context.Posts.Where(p => p.Blog.Rating > 5); 29 foreach (var post in query) 30 { 31 post.Title += "[Cool Blog]"; 32 } 33 34 await context.SaveChangesAsync(); 35 } 36 } 37 } 38 } 39 } 40 }

?

目前,使用TransactionScope還有一些限制:

  • 需要.NET 4.5.1及以上版本才支持異步方法;
  • 不能適用于云方案(除非你確保只有一個連接 -- 云方案不支持分布式事務);
  • 不能和Database.UseTransaction()結合使用;
  • 如果你的DDL代碼存在問題(例如數據庫初始化問題)或沒有通過MSDTC服務來支持分布式事務,將拋出異常;

使用TransactionScope的優點:

  • 自動將本地事務升級為分布式事務:前提是你有不止一個連接到給定數據庫或要組合一個連接到另一個數據庫連接到同一事務(注意:你必須啟動MSDTC服務以支持分布式事務)。
  • 易于編程。如果你更希望淡化對事務的關注,而非顯示操作事務,使用TransactionScope將是一個更合適的選擇。

?

????? 隨著EF6提供了Database.BeginTransaction()和Database.UseTransaction() 兩個API,使用TransactionScope不在是必須的了。如果你依然使用TransactionScope,就必須留意上面限制。建議你盡可能使用新的API,而非TransactionScope。

總結

以上是生活随笔為你收集整理的了解Entity Framework中事务处理的全部內容,希望文章能夠幫你解決所遇到的問題。

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