在事务中使用 "GO"
在事务中使用 "GO"
我正在构建一个Web应用程序,试图在App_Start中安装/升级数据库。\n安装过程的一部分是确保数据库已经安装了asp.net特性。为此,我使用System.Web.Management.SqlServices对象。\n我的意图是在SQL事务中完成所有的数据库工作,如果其中任何部分失败,回滚事务并保持数据库不变。\nSqlServices对象有一个方法“Install”,它接受一个ConnectionString,但没有事务。所以我使用SqlServices.GenerateApplicationServicesScripts来实现:\n
string script = SqlServices.GenerateApplicationServicesScripts(true, SqlFeatures.All, _connection.Database); SqlHelper.ExecuteNonQuery(transaction, CommandType.Text, script, ...);
\n然后我使用Enterprise Library中的SqlHelper。\n但是这会抛出一个带有大量错误的异常,其中一些错误如下:\n
Incorrect syntax near 'GO'. Incorrect syntax near 'GO'. Incorrect syntax near 'GO'. Incorrect syntax near 'GO'. Incorrect syntax near the keyword 'USE'. Incorrect syntax near the keyword 'CREATE'. Incorrect syntax near 'GO'. The variable name '@cmd' has already been declared. Variable names must be unique within a query batch or stored procedure.
\n我猜测这可能是在SQL事务中使用GO语句的问题。\n在执行这种方式时,如何使生成的脚本工作?
在处理包含"GO"关键字的SQL脚本时,可能会出现问题。解决这个问题的一种简单方法是将SQL脚本按照"GO"关键字进行拆分,然后逐个执行。
首先,我们可以创建一个名为getCommands的方法,该方法接收一个包含SQL脚本的字符串作为参数。在这个方法中,我们将使用"GO\r\n"作为分隔符,将SQL脚本拆分成多个命令,并将其存储在一个字符串数组中。
接下来,我们将使用List
接下来,我们可以通过循环遍历命令列表,并使用ExecuteNonQuery()方法逐个执行命令。
如果想要进一步优化,可以考虑使用事务来确保所有命令要么全部执行成功,要么全部失败。可以有两种处理事务的方式:
a) 将整个执行过程包装在一个事务中,如果希望所有命令要么全部执行成功,要么全部失败,这是一个更好的选择。
b) 每个ExecuteNonQuery()方法调用都在自己的事务中执行。如果发生异常,可以捕获异常并继续下一个命令的执行。不过,如果命令之间存在依赖关系,一个命令的失败可能会导致整个过程失败。
如果在脚本的某个部分发生错误,如何回滚更改?可以使用一个单一的事务来回滚整个过程。
需要注意的是,上述方法在拆分SQL脚本时可能会出现问题,例如,如果有包含"GO"关键字的注释等。因此,需要谨慎使用。另外,还可以使用StringReader来实现对SQL的读取和拆分,并且对注释等更加敏感。
总结一下,通过拆分包含"GO"关键字的SQL脚本,并逐个执行拆分后的命令,可以解决这个问题。如果需要对整个过程进行回滚,可以使用事务来确保所有命令要么全部执行成功,要么全部失败。
使用"GO"在事务中的原因是为了将多个操作作为一个单元进行回滚。在SQL Server Management Studio等工具中,可以使用"GO"将多个操作组织在同一个事务中,从而实现批量回滚。但是,在C#等其他语言中,"GO"并不是有效的SQL语句,因此无法在其中使用该方法。
解决方法是将需要进行批量回滚的操作放在一个存储过程中。下面是一个示例:
CREATE PROCEDURE MyProcedure
AS
BEGIN
BEGIN TRANSACTION TransactionWithGos;
GO
SET XACT_ABORT ON; -- 如果脚本中发生错误,回滚所有操作
GO
-- 执行操作1
GO
-- 执行操作2
GO
COMMIT TRANSACTION TransactionWithGos;
GO
END
需要注意的是,上述示例中的代码是无效的,因为在"GO SET XACT_ABORT"之后,"XACT_ABORT"会返回到默认的OFF状态。