2010-05-27 189 views
63

我有一個在TransactionScope中運行的代碼塊,在這段代碼中,我對數據庫進行了幾次調用。選擇,更新,創建和刪除整個色域。當我執行我的刪除時,我使用SqlCommand的擴展方法執行它,如果它死鎖,將自動重新提交查詢,因爲此查詢可能會造成死鎖。TransactionScope過早完成

我相信當遇到死鎖並且函數試圖重新提交查詢時會發生問題。這是我收到的錯誤:

與當前連接關聯的事務已完成但尚未處理。事務必須在連接可用於執行SQL語句之前進行處理。

這是簡單的代碼執行查詢(所有代碼的使用TransactionScope的內下面執行):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App)) 
{ 
    sqlCommand.Connection.Open(); 
    sqlCommand.ExecuteNonQueryWithDeadlockHandling(); 
} 

這裏是重新提交膠着查詢擴展方法:

public static class SqlCommandExtender 
{ 
    private const int DEADLOCK_ERROR = 1205; 
    private const int MAXIMUM_DEADLOCK_RETRIES = 5; 
    private const int SLEEP_INCREMENT = 100; 

    public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand) 
    { 
     int count = 0; 
     SqlException deadlockException = null; 

     do 
     { 
      if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT); 
      deadlockException = ExecuteNonQuery(sqlCommand); 
      count++; 
     } 
     while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES); 

     if (deadlockException != null) throw deadlockException; 
    } 

    private static SqlException ExecuteNonQuery(SqlCommand sqlCommand) 
    { 
     try 
     { 
      sqlCommand.ExecuteNonQuery(); 
     } 
     catch (SqlException exception) 
     { 
      if (exception.Number == DEADLOCK_ERROR) return exception; 
      throw; 
     } 

     return null; 
    } 
} 

上線會出現錯誤:

sqlCommand.ExecuteNonQuery(); 

回答

59

不要忘記從你的TransactionScope中壓縮你的select語句。在SQL Server 2005及更高版本中,即使使用(nolock)時,仍會在選定的表格上創建鎖。檢查了這一點,它會顯示how to setup and use TransactionScope

using(TransactionScope ts = new TransactionScope 
{ 
    // db calls here are in the transaction 
    using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) 
    { 
    // all db calls here are now not in the transaction 
    } 
} 
+1

感謝您的技巧抑制選擇語句上的交易。這有助於解決超時問題,這讓我發瘋。 – 2011-08-16 16:10:24

+2

夢幻般的答案。這讓我瘋狂的選擇/插入集合的SQL指令的集合。添加Suppress選項可自動解決問題。 – IoChaos 2012-02-23 22:31:00

+1

神聖的廢話謝謝你!我一整天都在摔跤。這種簡單的解決方案。 – rossipedia 2012-10-05 03:03:06

10

如果在TransactionScope內發生異常,它將回滾。這意味着TransactionScope已完成。您現在必須致電dispose()並開始新的交易。我真的不知道你是否可以重複使用舊的TransactionScope,我從來沒有嘗試過,但我不認爲。

+2

即使異常被捕獲,事務回滾? – Chris 2010-05-27 14:55:21

+0

我從來沒有嘗試過它,例外=錯誤=停止和回滾。但是,從你所描述的內容看來,這似乎是如此。 – Donnie 2010-05-27 15:22:37

+1

這是錯誤的,而不是異常情況。您也不必調用Dispose()。當在using語句中生成TransactionScope時,using語句將Dispose()處理Exceptions上的TransactionScope。 – thewhiteambit 2015-03-26 13:34:55

20

我可以重現該問題。這是一個事務超時。

using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1))) 
{ 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 
     using (var sqlCommand = connection.CreateCommand()) 
     { 
      for (int i = 0; i < 10000; i++) 
      { 
       sqlCommand.CommandText = "select * from actor"; 
       using (var sqlDataReader = sqlCommand.ExecuteReader()) 
       { 
        while (sqlDataReader.Read()) 
        { 
        } 
       } 
      } 
     } 
    } 
} 

拋出System.InvalidOperationException此消息:「與當前連接相關聯的事務已完成 但尚未設置事務必須被佈置 之前的連接可用於執行SQL語句。」。

要解決該問題,可以使查詢運行得更快或增加超時。

6

我的問題是一個愚蠢的,如果你坐在調試突破超時,你會得到這個。 面對棕櫚

人,節目讓你覺得厚了些日子......

39

我發現,當一個交易,比maxTimeoutSystem.Transactions較長時間運行可能會出現此消息。 TransactionOptions.Timeout增加並不重要,它不能超過maxTimeout

maxTimeout的默認值被設定爲10分鐘,並且其值可以可以在machine.config

改性添加(在配置電平)以下到machine.config修改超時:

<configuration> 
    <system.transactions> 
     <machineSettings maxTimeout="00:30:00" /> 
    </system.transactions> 
</configuration> 

在Machine.config,可以發現:%windir%\Microsoft.NET\Framework\[version]\config\machine.config

您可以在此博客帖子閱讀更多關於它:http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

+3

請記住,配置文件區分大小寫,以便它應該是「machineSettings」和「maxTimeout」。太糟糕了,你不能在你的app.config文件中覆蓋這個:( – 2012-10-10 10:06:29

+1

還要注意,你*必須*把它放在配置部分的*端*,否則你會得到一個錯誤 – 2015-06-28 17:29:16

6

確認此錯誤也可能是由事務超時造成的。爲了補充Marcus + Rolf的陳述,如果您沒有明確設置TransactionScope的超時時間,TimeSpan的超時時間將假定爲默認值。此默認值是的

  1. 如果你已經覆蓋了當地的app.config/web.config設置,例如

    <system.transactions> 
    <defaultSettings timeout="00:05:00" /> 
    </system.transactions> 
    
  2. 但是在machine.config然後這被 '封頂' 設置<machineSettings maxTimeout="00:10:00" />

+0

我可以_override_這個* ** app.config文件***? – Kiquenet 2017-03-31 10:41:07

1

此異常也可以通過禁用Microsoft Distributed Transaction Coordinator引起的。

如果我們想啓用它,我們運行「DCOMCNFG」,然後選擇"Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC"並選擇「屬性」。

應檢查「允許遠程客戶端」,「允許入站」,「允許出站」和「不要求進行驗證」。

+0

您可以使用'BAT'或'ps1'啓用/禁用*** _programmatically_嗎? – Kiquenet 2017-03-31 10:46:53