我使用C#和ADO.Net与TransactionScope
在ASP.Net应用程序中运行事务。此事务应该在多个表中保存一些数据,然后向订阅者发送电子邮件。
问题:是否有效使用TransactionScope
,当它包含对SQL Server 2014中具有自己的事务的存储过程的调用时,或者我应该删除SQL事务在begin tran
中调用存储过程的commit tran
,rollback tran
和TransactionScope
语句?
此场景的C#代码以及存储过程的T-SQL代码都在下面提到。
使用TransactionScope
的C#代码:
try
{
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// SaveEmailData is a stored procedure that has a transaction within it
SqlCommand command1 = new SqlCommand("SaveEmailData", connection1);
command1.CommandType = CommandType.StoredProcedure;
command1.ExecuteNonQuery();
}
//Send Email using the helper method
EmailHelper.SendCustomerEmails(customerIds);
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch( Exception ex)
{
Logger.Log(ex);
}
存储过程的{T-SQL SaveEmailData
:
SET NOCOUNT ON
BEGIN TRY
DECLARE @emailToUserId BIGINT
BEGIN TRAN
-- //update statement. detail statement omitted
UPDATE TABLE1...
--update statement. detail statement omitted
UPDATE TABLE2...
IF @@trancount > 0
BEGIN
COMMIT TRAN
END
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRAN
END
EXEC Error_RaiseToADONET
END CATCH
答案 0 :(得分:14)
是的,TransactionScope
在包装TSQL BEGIN / COMMIT TRANSACTION
或ADO SqlConnection.BeginTransaction
时仍然可以正常工作。包装单个连接时,行为类似于Sql
中的嵌套事务:
@@TranCount
将在每个BEGIN TRAN
COMMIT TRAN
只会减少@@TRANCOUNT
。只有在@@TRANCOUNT
达到零时,才会提交交易。
然而:
ROLLBACK TRAN
将中止整个交易(即@@TRANCOUNT to zero),除非您使用Save Points(即SAVE TRANSACTION xx
... ROLLBACK TRANSACTION xx
。@@TRANCOUNT
在退出SPROC时与输入SPROC时的值不同,则会收到错误。因此,通常更容易将事务语义留给TransactionScope
并删除任何手动BEGIN TRAN / COMMIT TRAN
逻辑,使您的TSQL变得混乱。
修改 - 澄清以下评论
在OP的情况下,SPROC并未编写嵌套事务(即,是否由Sql或.Net外部事务包装),特别是ROLLBACK
BEGIN CATCH
} block将中止整个外部事务,并且可能会导致外部TransactionScope
中的更多错误,因为@@TRANCOUNT
规则尚未遵守。如果SPROC需要以嵌套或独立的交易方式运作,则应观察nested transaction pattern such as this。
SavePoints do not work with Distributed transactions,TransactionScope
可以轻松escalate into a distributed transaction,例如如果您在事务范围内使用不同的连接字符串或控制其他资源。
因此,我建议将PROC重构为一个“快乐”的核心/内部情况,从事务范围调用此内部proc,并在那里进行任何异常处理和回滚。如果你还需要从Ad Hoc Sql调用proc,那么提供一个外部包装器Proc,它具有异常处理:
-- Just the happy case. This is called from .Net TransactionScope
CREATE PROC dbo.InnerNonTransactional
AS
BEGIN
UPDATE TABLE1...
UPDATE TABLE2 ....
END;
-- Only needed if you also need to call this elsewhere, e.g. from AdHoc Sql
CREATE PROC dbo.OuterTransactional
AS
BEGIN
BEGIN TRY
BEGIN TRAN
EXEC dbo.InnerNonTransactional
COMMIT TRAN
END TRY
BEGIN CATCH
-- Rollback and handling code here.
END CATCH
END;