如何使Azure服务总线客户端在发送消息时不参与环境事务

Ren*_*eno 3 c# azure azureservicebus .net-core

据我所知,Azure 服务总线不支持 DTC,如果您尝试这样做,您会收到如下异常:“其他资源管理器/DTC 不支持本地事务。” '

我的问题是,我需要向服务总线发送消息,并且代码可能与可能的数据库操作一起在事务范围内执行。但服务总线不需要特别成为该事务的一部分;因此,这里并不真正需要 DTC。然而,服务总线客户端似乎自动参与环境事务,从而将该事务提升为 DTC。

例子:

这运行正确(服务总线代码是事务中唯一的代码):

        using (var tx = new TransactionScope())
        {
            //A simple Azure Bus operation
            var builder = new ServiceBusConnectionStringBuilder(connectionString);
            var queueClient = new QueueClient(builder);
            var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
            messageBody.MessageId = Guid.NewGuid().ToString("N");
            queueClient.SendAsync(messageBody).GetAwaiter().GetResult();                

            tx.Complete();
        }
Run Code Online (Sandbox Code Playgroud)

但从另一个系统参与(这里是 Sql 连接)的那一刻起,“ Azure 服务总线不支持 DTC ”-抛出异常:

        using (var tx = new TransactionScope())
        {
            //A simple DB operation
            SqlConnection sqlConnection = new SqlConnection(dbConnectionString);
            sqlConnection.Open();
            SqlCommand cmd = new SqlCommand("INSERT INTO [dbo].[Table_1]([Name]) values ('Hello')", sqlConnection);
            cmd.ExecuteNonQuery();

            //A simple Azure Bus operation
            var builder = new ServiceBusConnectionStringBuilder(connectionString);
            var queueClient = new QueueClient(builder);
            var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
            messageBody.MessageId = Guid.NewGuid().ToString("N");
            queueClient.SendAsync(messageBody).GetAwaiter().GetResult();

            queueClient.CloseAsync().GetAwaiter().GetResult();
            sqlConnection.Close();

            tx.Complete();
        }
Run Code Online (Sandbox Code Playgroud)

此错误是可以理解的,并且已在此处进行了解释。但是有没有办法告诉服务总线客户端忽略环境事务呢?

Sea*_*man 5

您将需要抑制环境事务并使用以下内容包装您的服务总线代码:

public async Task Method()
{
  SqlConnection sqlConnection = new SqlConnection(dbConnectionString);
  sqlConnection.Open();

  using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
  {
    SqlCommand cmd = new SqlCommand("INSERT INTO [dbo].[Table_1]([Name]) values 
('Hello')", sqlConnection);
    await cmd.ExecuteNonQueryAsync();

    using (var tx = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
    {
       var builder = new ServiceBusConnectionStringBuilder(connectionString);
       var queueClient = new QueueClient(builder);
       var messageBody = new Message(Encoding.UTF8.GetBytes("Hello"));
       messageBody.MessageId = Guid.NewGuid().ToString("N");
       queueClient.SendAsync(messageBody).GetAwaiter().GetResult();

       queueClient.CloseAsync().GetAwaiter().GetResult();

       tx.Complete();
    }

    tx.Complete();
  }
  sqlConnection.Close();
}
Run Code Online (Sandbox Code Playgroud)

注意

  1. 您不应该每次都重新创建队列客户端。出于性能考虑保留它。
  2. 不要在同步代码中使用异步 API。而是将您的方法转换为异步方法。SQL 操作与服务总线一样受 IO 限制。最好让方法是异步的。