2017-02-16 72 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