TransactionScope是一個分布式事務的語句塊,被包含起來的語句一起被提交,當出現異常,一起回滾,這都是托管的
當Web沒有開啟MSDTC服務時候會出現:
而對於你的事務塊,如果這個MSDTC服務被掛了后,如果事務塊包含誇庫操作,它將會被自動提升為MSDTC分布式事務
這時你的整個代碼塊會中斷,拋出異常
注意:什么時間會把你的Transactionscope提升為分布式事務?即要使用MSDTC?
1 當你的web服務器與數據庫服務器在同台電腦上,對同一個庫進行操作時候,它不會提升為分布式事務
2 web服務器與數據庫服務器在同台電腦,對於同一個庫,建立多個數據上下文時候,它不會提升為分布式事務
3 當web服務器與數據庫服務器在同台電腦上,當操作兩個庫的表,這時候才會提升為分布式事務
4 當web服務器與數據庫服務器不在同台電腦上,每次都會引發MSDTC錯誤
EF 什么時候會認為我們的TransactionScope是分布式的?
對於數據庫多表操作來說:它何時是分布式?要看事務中的Connection會話的數量,如果是一次,會認為在操作一個數據庫,肯定不會引入MSDTC,但如果是多個Connection會話,會認為在操作多張表,這時它屬於分布式的范圍,同時會引起MSDTC
為什么在項目中,對於操作同一個數據庫的多張表,總會產生MSDTC?
因為對於LinqToSql或者EF等ORM來說,它們會提供一個SubmitChanges,作用是打開Connection,將操作內容提交到數據庫,關閉Connection
因為一般都會對CURD進行封裝,如果同時封裝了SubmitChange,這時候系統會認為是一次完成的Connection,而進行第二個Insert等操作時候,這時會有兩個Connection會話,而在Transaction范圍內,對於多個Connection的理解就是多個庫,所以系統會把它提升為分布式事務,開啟用MSDTC去支持它,對於我們來說,不希望每個Transaction都產生MSDTC,
解決方法很簡單:
將CURD進行重裝,加個參數ISSubmit就可以:表示如果為false,不立即提交數據
例如:
/// <summary>
/// 插入操作
/// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
/// <param name="entity">實體</param>
/// <param name="isSubmit">是否同時提交到數據庫,對於寫在transactionscope里的多條語句,
/// 如果本語句不是最后一條,本值為false,即不立即提交到數據</param>
public void Insert<TEntity>(TEntity entity, bool isSubmit) where TEntity : class
{
if (entity is EntityBase)
(entity as EntityBase).OnInsertBefore();
//Logger.InfoLog("Create 表名:{0},內容:{1}", entity, ToJson(entity));
_db.Entry<TEntity>(entity);
_db.Set<TEntity>().Add(entity);
if (isSubmit)
this.SaveChanges();
if (entity is EntityBase)
(entity as EntityBase).OnInsertAfter();
}
對於具體的事務塊,可能這樣:
using (TransactionScope trans = new TransactionScope())
{
try
{
base.Insert<Product_Comment>(entity.Product_Comment, false);
base.Insert<Review>(entity.Review);
trans.Complete();
}
catch (Exception)
{
throw;
}
finally
{
trans.Dispose();
}
}
總結:對於Web服務器與數據庫服務器不在一起電腦上,產生MSDTC的關鍵在於Connection會話的數量,一個會話表示操作一個數據庫,這時不產生MSDTC,如果是多個會話,認為是操作多個數據庫,所以會產生MSDTC的分布式的問題