【发布时间】:2014-07-30 09:34:19
【问题描述】:
我们遇到了这样一种情况,即适用于 VS 2013 的 SSDT 在 90% 的情况下运行良好,但在测试的存储过程回滚事务时却失败了。
因为 SQL Server 并没有真正的嵌套事务,所以回滚内部事务会回滚任何外部事务。通常这不是问题,但在 SSDT 单元测试的上下文中,我们通过将单元测试方法包装在 TransactionScope / 回滚中来采取稍微定制的方法。这就是导致问题的原因,同时解决了与单元测试数据清理后测试相关的问题。
为了在我们的程序中出现需要回滚的情况时解决此问题,我们在程序结束之前添加了一个BEGIN TRANSACTION。这平衡了事务计数,并且通过我们进行的一些测试来检查前后过程 exec,它似乎确保了最后一个 BEGIN 上的隐式 COMMIT 已完成。
问题是:在正常操作下(即没有被调用中间件中的TransactionScope 包围),这似乎会引发存储过程 exec 之后的任何语句的问题同一批次。从 ORM(如 EF)或直接使用 ADO.NET 调用存储过程时是否存在问题?该语句批处理肯定会终止,留下已打开事务的隐式完成吗?
例如/引用存储过程将包含如下内容:
CREATE PROCEDURE [dbo].[ProcName]
/* params */
AS
SET NOCOUNT ON
SET XACT_ABORT ON
/* set var according to transcount check*/
DECLARE @IsInsideTransaction BIT = CASE WHEN @@trancount > 0 THEN 1 ELSE 0 END
/* apply new information to data store */
begin TRANSACTION
declare @RetVal int = 0
begin try
insert into dbo.AMessageTable
(/* some columns */)
values
(/* some values */)
set @messageId = scope_identity()
commit transaction
set @retVal = @messageId
end try
begin catch
SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage;
SET @RetVal = -1;
ROLLBACK TRANSACTION
/* ### this gets us out of the transaction count issue within the UT ### */
IF @IsInsideTransaction = 1
BEGIN
BEGIN TRANSACTION
END
end catch
return @retVal
GO
我们修改自动生成的 SSDT 单元测试代码以包含如下内容:
[TestInitialize()]
public void TestInitialize() {
_transaction = new TransactionScope(TransactionScopeOption.RequiresNew);
base.InitializeTest();
}
[TestCleanup()]
public void TestCleanup() {
base.CleanupTest();
if (_transaction != null) {
_transaction.Dispose();
}
}
【问题讨论】:
标签: sql stored-procedures transactions sql-server-2012 sql-server-data-tools