2017-02-16 71 views
2

我一直在將​​放入SQL命令語句中,並注意到它不會回滾更新,插入等在我的C#SqlCommand上出錯。取自this的帖子。 MSDN指出:SQL錯誤不回滾整個SqlCommand

當SET XACT_ABORT爲ON時,如果Transact-SQL語句引發運行時錯誤,則整個事務終止並回滾。

我的SQL查詢的一般格式爲:

set xact_abort on 
INSERT INTO Table1 
UPDATE Table2 
UPDATE Table3 
UPDATE Table4 
--Finally 
SELECT someValue 

我注意到,在錯誤沒有被捲起我的SQL命令回來。特定的錯誤是這種情況是一個參數的數據長度超過了列指定的長度。我正在使用SqlCommandSqlParameter來創建SQL查詢。

我不想處理SQL中的異常,但任何錯誤都不會對數據庫進行任何更改是非常重要的。

通常的錯誤包括:列不存在,錯誤的數據類型,數據將被截斷,由於長度等

我應該使用比​​其他的東西嗎?提前致謝!

+0

爲什麼不在你的C#中捕捉'SqlException'並手動回滾事務? – Andrew

+4

據底部[此鏈接](https://msdn.microsoft.com/en-us/library/ms188792.aspx),你仍然需要包裝的聲明中呼籲'BeginTransaction'和'EndTransaction'。似乎它只是節省了你不得不對錯誤做出反應並自己調用'Rollback'。 – DonBoitnott

+0

如果您正在運行多個腳本,另一種管理方式是[dbup](http://dbup.github.io)。 – lloyd

回答

3

您從MSDN引用的說法是正確的:與XACT_ABORT設置爲ON任何錯誤將中止該批處理並回滾任何活動的事務。這裏的困惑是,在SQL Server中,默認情況下,每個語句本身都是一個事務。如果您想要將多個報表組合爲明確的事務處理,那麼您需要使用BEGIN TRAN;以及COMMIT;。下面的例子說明了這種行爲:

運行此:

SET XACT_ABORT ON; 
CREATE TABLE #Bob (ID INT); 

INSERT INTO #Bob (ID) VALUES (1); 

BEGIN TRAN; 

INSERT INTO #Bob (ID) VALUES (2); 
INSERT INTO #Bob (ID) VALUES (3); 
INSERT INTO #Bob (ID) VALUES (4/0); 

COMMIT TRAN; 

然後單獨運行此(由於在語句上述錯誤將中止整批 - 由於使用XACT_ABORT ON - 等等SELECT如果你試圖在同一時間運行SELECT永遠不會執行):

SELECT * FROM #Bob; 

它會返回一個包含1單行因爲本身而不是機智執行該語句暗示明確的交易。一旦將BEGIN TRAN;COMMIT TRAN;語句添加到您的代碼中,它將按照您期望的那樣工作。應對來自C#代碼交易

+0

謝謝!甚至沒有意識到你提到的微妙之處,並且迄今爲止還沒有在我的SQL查詢中使用過事務。我現在也調查了一些SQL事務錯誤處理,這對於驗證和解決多個語句查詢有很大的幫助。 – Matt

1

一種模式如下:

try 
{ 
    var connection = new SqlConnection(connectionString); 
    connection.Open(); 

    var trans = connection.BeginTransaction(); 

    using (var command = connection .CreateCommand()) 
    { 
     command.Transaction = trans; 
     command.CommandText = "..."; 
     command.ExecuteNonQuery(); 
    } 

    // other commands may be defined here 
    // command can be included or excluded from transaction (do not set Transaction property) 

    // commits the transaction 
    trans.Commit(); 
} 
// best practice is to catch specific exception types like `SqlException` 
catch (Exception ex) //error occurred 
{ 
    trans.Rollback(); 
    // log error somewhere 
} 
finally 
{ 
    // execute no-matter what 
} 

這種模式具有以下優點:

  1. 沒有必要擔心SET XACT_ABORT(默認關閉)
  2. 更好的異常處理

注:你可能感興趣Unit of Work pattern

+0

謝謝,甚至沒有意識到SqlCommand事務!即使它可能不是我原來的問題的解決方案,它可能是更好的方法,因爲我已經使用了SqlCommands。 – Matt

+0

這是你的_problem_的答案,而不是你的_question_。如果您使用C#代碼管理事務,則更容易理解失敗(例外管理,日誌記錄)。當然,如果你堅持用SQL編寫所有東西,接受的答案是好的。但是,最好的做法是在SQL中使用'try .. catch'塊,如[這裏]所示(http://stackoverflow.com/a/25147029/2780791)。 – Alexei