【问题标题】:Why is MSDTC involved?为什么涉及 MSDTC?
【发布时间】:2013-04-23 20:39:02
【问题描述】:

据我所知,MSDTC 仅在以下情况下介入:

  • 您正在查询链接到的事务中的视图/表 另一台服务器。

  • 您正在使用两个 SqlConnection(或其他任何 NHibernate 使用)在单个 TransactionScope 中

  • 您在 TransactionScope 中征用另一个事务组件(如 MSMQ 或事务文件系统)。

  • 未提及的其他情况。

如果我禁用 MSDTC 并运行以下代码,我会收到(服务器“服务器”上的 MSDTC 不可用)错误。

public bool Add(PurchaseOrderInfo purchaseOrderInfo)
{
    using (TransactionScope ts = new TransactionScope())
    {
        using (SqlConnection Cnn = new SqlConnection(SqlHelper.ConnStr))
        {
            Cnn.Open();

                try
                {
                    using (SqlDataReader rdr = SqlHelper.ExecuteReader(/*Tr*/Cnn, "spPurchaseOrderAdd", purchaseOrderInfo.ExpectedShipment.ShipmentID, purchaseOrderInfo.CreateDate, purchaseOrderInfo.CustomerNotes, purchaseOrderInfo.Status, purchaseOrderInfo.PurchaseOrderNumber))
                    {
                        if (rdr.Read())
                            FillPurchaseOrderInfo(rdr, ref purchaseOrderInfo, GettingDepthEnum.Level_0);
                        else
                        {
                            return false;
                        }
                    }

                    foreach (PurchaseOrderDetailInfo detailInfo in purchaseOrderInfo.Details)
                    {
                        throw new Exception("Test");
                        //if (!AddPurchaseOrderDetail(Tr, purchaseOrderInfo, detailInfo))
                        {
                            //Tr.Rollback();
                            return false;
                        }
                    }

                    return true;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                ts.Complete();
            }
        }
    }

我错过了什么吗?

更新: 存储过程包含一个简单的插入语句:

INSERT INTO tblPurchaseOrder
(ShipmentID, CreateDate, CustomerNotes, PurchaseOrderState, PurchaseOrderNumber, LastActivityDate)
VALUES
(@ShipmentID, @CreateDate, @CustomerNotes, @PurchaseOrderState, @PurchaseOrderNumber, GETDATE());

--Step 2: return row that INSERTED to Client Computer.
SELECT dbo.viwGetPurchaseOrderWeight.Weight,* FROM tblPurchaseOrder LEFT OUTER JOIN viwGetPurchaseOrderWeight ON viwGetPurchaseOrderWeight.PurchaseOrderID =  tblPurchaseOrder.PurchaseOrderID WHERE (tblPurchaseOrder.PurchaseOrderID = Scope_Identity());

更新 2: 关闭MSDTC时,执行到此行时抛出异常:

using (SqlDataReader rdr = SqlHelper.ExecuteReader(/*Tr*/Cnn, "spPurchaseOrderAdd", purchaseOrderInfo.ExpectedShipment.ShipmentID, purchaseOrderInfo.CreateDate, purchaseOrderInfo.CustomerNotes, purchaseOrderInfo.Status, purchaseOrderInfo.PurchaseOrderNumber))

这意味着后面的行没有效果。

【问题讨论】:

  • 你的进程在做什么?它们是否涉及链接服务器?
  • @DavidHoerster 我更新了问题以包含该过程。
  • 您是否使用了第二个数据库连接?看起来不是这样,但我相信如果在TransactionScope 中引入第二个连接,那么事务就会被提升到 DTC。否则它应该留在本地。
  • @DavidHoerster 这正是让我感到困惑的地方!没有其他联系! SqlHelper 是 Microsoft.ApplicationBlocks.Data 程序集中的一个类,我不认为它是在内部打开连接!
  • 发布精简的代码示例会更容易获得答案。尝试摆脱所有的调试问题。

标签: c# sql-server-2008 transactions msdtc


【解决方案1】:

请确保不要再次打开连接,

using (TransactionScope transactionScope = new TransactionScope()) {
    using (SqlConnection connection = new SqlConnection(connectionString)) {
    connection.Open();
    connection.Close();
    connection.Open(); // escalates to DTC
      }
}

【讨论】:

  • 不确定与问题的关系。当然,这段代码会促进,但他知道这一点。他想知道为什么他的代码会推广。
【解决方案2】:

根据您的堆栈跟踪帖子,EntLib 类正在打开另一个连接。我们需要将其从交易中排除。将您对 ExecuteReader 的调用封装在抑制事务范围内:

SqlDataReader rdr;
using (var tsSuppress = new TransactionScope(TransactionScopeOption.Suppress))
    rdr = SqlHelper.ExecuteReader(...)

这将暂时将 Transaction.Current 设置为 null,以便新连接不会接收事务。

顺便说一句,堆栈跟踪使我们能够找到问题的根本原因。

【讨论】:

  • 抑制事务会阻止插入过程成为其中的一部分吗?如果是这样,我不能禁止交易,因为整个方法应该作为一个部分成功或失败。
  • 命令永远不是事务的一部分。连接是。当您打开连接时,它会自动登记,因为此时 Transaction.Current 不为空。无论您做什么,它都会永远保持在 tran 中。
【解决方案3】:

检查堆栈跟踪后,似乎 SQLHelper 类正在打开一个连接以从数据库中获取存储过程所需参数的列表:

   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.TdsParser.GetDTCAddress(Int32 timeout, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlInternalConnectionTds.GetDTCAddress()
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at Microsoft.ApplicationBlocks.Data.SqlHelperParameterCache.DiscoverSpParameterSet(SqlConnection connection, String spName, Boolean includeReturnValueParameter)
   at Microsoft.ApplicationBlocks.Data.SqlHelperParameterCache.GetSpParameterSetInternal(SqlConnection connection, String spName, Boolean includeReturnValueParameter)
   at Microsoft.ApplicationBlocks.Data.SqlHelperParameterCache.GetSpParameterSet(SqlConnection connection, String spName, Boolean includeReturnValueParameter)
   at Microsoft.ApplicationBlocks.Data.SqlHelperParameterCache.GetSpParameterSet(SqlConnection connection, String spName)
   at Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(SqlConnection connection, String spName, Object[] parameterValues)
   at AlefTextileProduction.SQLServerDAL.PurchaseOrder.Add(PurchaseOrderInfo purchaseOrderInfo) 

我正在尝试解决这个问题!

【讨论】:

    【解决方案4】:

    在查看SQLHelper 的代码时,看起来如果您使用ExecuteReader 的重载,它采用SqlParameter 实例而不仅仅是参数值,那么您将保存SQLHelper 尝试执行的步骤找出什么参数映射到什么值 - 从而节省第二次访问数据库和第二次连接以及将您的事务提升到 MSDTC。

    根据code I'm looking at,这是您可能应该调用的方法:

    Public Overloads Shared Function ExecuteReader(ByVal connectionString As String, _
                     ByVal commandType As CommandType, _
                     ByVal commandText As String, _
                     ByVal ParamArray commandParameters() As SqlParameter) As SqlDataReader
    

    如果这不起作用,那么我建议您推出自己的 DAL。 :)

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2018-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多