如何在SQLCLR中使用TransactionScope而不升级到MSDTC

5
很多我们的DAL代码使用TransactionScope来进行事务处理。这个方法很好用,但当我从SQLCLR过程中使用此DAL代码时,会出现一个问题——事务被升级到MSDTC,而这不是我想要的。
这个问题很容易复现:
  1. CLR Implementation

    [SqlProcedure]
    public static void ClrWithScope(string cmdText)
    {
        /* escalates to MSDTC when a transaction is already open */
        using ( var scope = new TransactionScope())
        {
            using (var connection = new SqlConnection("context connection=true;"))
            {
                connection.Open();
                using (var cmd = new SqlCommand(cmdText, connection))
                {
                    SqlContext.Pipe.ExecuteAndSend(cmd);
                }
            }
            scope.Complete();
        }
    }
    
    [SqlProcedure]
    public static void ClrWithTrans(string cmdText)
    {
        /* works as expected (without MSDTC escalation ) */
        using (var connection = new SqlConnection("context connection=true;"))
        {
            connection.Open();
            using (var tx = connection.BeginTransaction())
            {
                using (var cmd = new SqlCommand(cmdText, connection, tx))
                {
                    SqlContext.Pipe.ExecuteAndSend(cmd);
                    tx.Commit();
                }
            }
        }
    }
    

  2. SQL script used to execute the CLR procedure

    BEGIN TRANSACTION
    
    exec dbo.ClrWithTrans "select * from sys.tables";
    exec dbo.ClrWithScope "select * from sys.tables"; /* <- DOES NOT WORK! */
    
    ROLLBACK TRANSACTION
    
  3. the error

    Msg 6549, Level 16, State 1, Procedure ClrWithScope, Line 0
    A .NET Framework error occurred during execution of user defined routine or aggregate 'clrClrWithScope': 
    System.Transactions.TransactionAbortedException: Die Transaktion wurde abgebrochen. ---> System.Transactions.TransactionPromotionException: MSDTC on server 'BLABLA' is unavailable. ---> System.Data.SqlClient.SqlException: MSDTC on server 'BLABLA' is unavailable.
    System.Data.SqlClient.SqlException: 
       bei System.Data.SqlServer.Internal.StandardEventSink.HandleErrors()
       bei System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote()
    System.Transactions.TransactionPromotionException: 
       bei System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote()
       bei System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
       bei System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
    System.Transactions.TransactionAbortedException: 
       bei System.Transactions.TransactionStateAborted.CreateAbortingClone(InternalTransaction tx)
       bei System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)
       bei System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)
       bei System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)
       bei System.Transactions.TransactionScope.PushScope()
       bei System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)
       bei Giag.Silo.Data.SqlClr.ClrWithScope(String cmdText)
    . User transaction, if any, will be rolled back.
    
没有“BEGIN TRANSACTION”语句,dbo.ClrWithScope调用正常。我认为在.NET Framework中注册时,SQLServer启动的事务不会被考虑。
有没有解决办法?一个想法是手动创建SqlTransaction并使TransactionScope使用此事务,但我不知道该如何实现。另一种解决方法是在所有DAL代码中进行特殊处理(实现起来并不好玩)。
有什么想法吗?

尝试将“Connection”放在“Scope”周围。 - Magnus
@Magnus 但是这并不会自动将连接加入环境事务中,而这正是TransactionScope的整个目的,不是吗? - Christian.K
Christian.K 是正确的。DAL 代码经常使用 TransactionScope 和 SqlConnection。当然,可以重写整个 DAL 代码库,但这并不好看。 - Stefan Schönbächler
也许我错过了什么,但为什么它会升级到DTC呢?这只会在连接/执行第二个服务器/实例时发生,但这里没有任何迹象吗? - RBarryYoung
@RBarryYoung。在某些情况下(嵌套、SqlServer版本),即使只使用一个连接字符串,事务也会升级。请参见[链接](https://dev59.com/tuo6XIcBkEYKwwoYTzEw?rq=1)获取更多信息。在这里,仅当在SQL脚本中使用BEGIN TRANSACTION时,事务才会升级。 - Stefan Schönbächler
相关链接:http://dba.stackexchange.com/q/14318/4675 - Danny Varod
1个回答

1

在SQL CLR中使用TransactionScope将始终升级/升级为MSDTC事务。即使在SQL 2012中也没有任何方法可以避免这种情况。

关于SQL CLR和TransactionScope的技术文档(http://technet.microsoft.com/en-us/library/ms131084.aspx

仅当访问本地和远程数据源或外部资源管理器时,才应使用TransactionScope。这是因为TransactionScope始终会导致事务升级,即使它仅在上下文连接内使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接