如何使 Azure 服务总线客户端在发送消息时不参与环境事务
How to make Azure Service Bus client to not participate to the ambient transaction when sending a message
我知道 Azure 服务总线不支持 DTC,如果您尝试这样做,您会遇到这样的异常:'其他资源 managers/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();
}
但是从另一个系统参与(这里是 Sql 连接)开始,“DTC 不受 Azure 服务总线支持”- 抛出异常:
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();
}
这个错误是可以理解的,并且已经解释过了 here。
但是有没有办法告诉服务总线客户端忽略环境事务?
您将需要抑制环境事务并使用以下内容包装您的服务总线代码:
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();
}
注意
- 您不应该每次都重新创建您的队列客户端。出于性能考虑,保留它。
- 不要在同步代码中使用异步 API。而是将您的方法转换为异步。 SQL 操作与服务总线一样受 IO 限制。方法最好是异步的。
我知道 Azure 服务总线不支持 DTC,如果您尝试这样做,您会遇到这样的异常:'其他资源 managers/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();
}
但是从另一个系统参与(这里是 Sql 连接)开始,“DTC 不受 Azure 服务总线支持”- 抛出异常:
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();
}
这个错误是可以理解的,并且已经解释过了 here。 但是有没有办法告诉服务总线客户端忽略环境事务?
您将需要抑制环境事务并使用以下内容包装您的服务总线代码:
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();
}
注意
- 您不应该每次都重新创建您的队列客户端。出于性能考虑,保留它。
- 不要在同步代码中使用异步 API。而是将您的方法转换为异步。 SQL 操作与服务总线一样受 IO 限制。方法最好是异步的。