SqlConnection和避免晋升到MSDTC

本文关键字:MSDTC SqlConnection | 更新日期: 2023-09-27 18:16:19

当我们需要在应用程序中访问数据库时,我们使用以下模式:

    对于查询,我们有一个静态工厂类,其方法CreateOpenConnection只做new SqlConnection(myConnectionString),并在其上调用Open()。在执行查询之前调用该方法,并在查询返回后处理连接。
  • 对于插入/更新/删除,我们使用工作单元模式,其中更改被批处理并通过调用work.Commit()提交给数据库,如下所示:

工作。提交:

using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    using (var conn = DapperFactory.CreateOpenConnection())
    {
      var count = _changeTracker.CommitChanges(conn);
      tranScope.Complete();
      return count;
    }
}

这似乎很适合作为web服务的一部分,但是当我试图将其与Rebus结合使用时,目前给我的MSDTC带来了麻烦。

据我所知,Rebus(当它处理队列中的消息时)创建了一个新的TransactionScope,以便在处理消息失败的情况下,可以回滚内容。到目前为止,这本身运行得很好。我可以在Rebus消息处理程序中打开一个新的SqlConnection而没有任何问题(然而,使用我们的传统实体框架查询手动SqlConnections在相同的Rebus TransactionScope中不起作用,但我现在不认为这是一个问题)。但是昨天我问了以下问题:

Rebus中某一消息类型的串行处理

这个问题的答案似乎是使用Rebus的传奇特征。我尝试实现这一点,并对其进行了配置,以便将Rebus传奇持久化到新的SQL Server数据库(具有不同的连接字符串)。大概,使用SQL Server持久性打开自己的SqlConnection,因为任何时候我尝试创建SqlConnection现在,我得到以下异常:

分布式事务管理器(MSDTC)的网络访问已被禁用。请使用组件服务管理工具在MSDTC的安全配置中启用DTC进行网络访问。

启用MSDTC是我非常,非常非常喜欢避免做的事情,关于配置和性能开销。也许我错了,但这似乎也没有必要。

我假设这里发生的是Rebus创建了一个环境TransactionScope,并且它创建的SqlConnection加入了该作用域。当我尝试创建自己的SqlConnection时,它也试图加入该环境作用域,因为涉及多个连接,它被提升为MSDTC,这失败了。

我有一个关于如何解决这个问题的想法,但我不知道这样做是否正确。我要做的是:

  • Enlist=false添加到我的应用程序的连接字符串,以便它永远不会加入环境事务。
  • 修改Commit方法,使其不创建新的TransactionScope(我的连接不再订阅,因为我刚刚告诉它不应该),但它使用conn.BeginTransaction

一样:

var transaction = conn.BeginTransaction();
try
{
  var count = _changeTracker.CommitChanges(conn);
  transaction.Commit();
  return count;
}
catch
{
  transaction.Rollback();
  throw;
}
finally
{
  transaction.Dispose();
}

我只是不确定这是否是正确的方法,以及可能的缺点是什么。

提示吗?

UPDATE:澄清一下,这不是work.Commit()一直给我的问题,我很确定它会起作用,但我从来没有到那里,因为我的查询是什么失败了。

失败的例子:

public int? GetWarehouseID(int appID)
{
  var query = @"
select top 1 ID from OrganizationUnits o
where TypeID & 16 = 16 /* warehouse */";
  using (var conn = _dapper.OpenConnection())
  {
    var id = conn.Query<int?>(query).FirstOrDefault();
    return id;
  }
}

当一个TransactionScope被Rebus创建时,以及一个SqlConnection被Rebus打开后,这个函数被调用。在打开my SqlConnection时,它试图登记并崩溃

SqlConnection和避免晋升到MSDTC

我有点惊讶你看到这个,因为RequiresNew 应该意味着它是与其他事务隔离的;通常,此消息意味着在事务范围内激活了2个连接-您是否确定在该块内没有其他代码创建/打开连接?

你提出的解决方案应该工作-虽然在某些方面TransactionScopeOption.Suppress可能比改变你的配置更方便(但两者都应该工作)。然而,这里有一个问题:ADO。. NET事务必须传递给各个命令,因此您需要(还需要整理一下代码):

using(var transaction = conn.BeginTransaction()) {
    try {
        var count = _changeTracker.CommitChanges(conn, transaction);
        transaction.Commit();
        return count;
    } catch {
        transaction.Rollback();
        throw;
    }
}

CommitChanges接受一个事务——可能使用可选参数:

int CommitChanges(DbConnection connection, DbTransaction transaction = null)
{ ... }

您对DapperFactory的命名表明您正在使用"dapper"-在这种情况下,您可以将其传递给"dapper",无论它是否为空,即

conn.Execute(sql, args, transaction: transaction);

这很大程度上取决于你使用的SQL Server的版本。请参阅这里的另一个SO问题,解决类似的问题。

这与SQL 2005和SQL 2008在处理同一TransactionScope内的多个连接时的不同有关。例如,SQL 2008可以在同一个TransactionScope中打开多个连接,而无需升级到MSDTC。

这是您看到的问题吗

如果是这种情况,我认为只有两个选择是升级到SQL 2008或启用MSDTC。我知道这两个选项可能都很令人头疼。