听起来像互联网上的大多数问题,但事情是这样的——当在单一环境中建立多个连接时
TransactionScope
它将被提升为 DTC。所以问题是,如何避免这种情况并在所有实例之间共享单一连接?有一个连接池机制(至少在Npgsql
)但我不确定它是否像我预期的那样工作。
这是一个复制品:https://github.com/uhfath/TestTransactionDbContext
该项目包含
Npgsql
和Sql Server
的测试方法。他们的行为不同:
Sql Server
我只是得到The operation is not valid for the state of the transaction
.Npgsql
查询根本不起作用。项目做的是:
TransactionScope
:private static TransactionScope CreateTransaction() =>
new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted
},
TransactionScopeAsyncFlowOption.Enabled);
private static async Task<Client> Create(IServiceProvider serviceProvider)
{
var client = new Client { Name = "client_1" };
await using var scope = serviceProvider.CreateAsyncScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AsyncDbContext>();
dbContext.Clients.Add(client);
await dbContext.SaveChangesAsync();
await Console.Out.WriteLineAsync($"Client: {client.Id}");
return client;
}
private static Random _randomer = new();
private static async Task Read(IServiceProvider serviceProvider, DependentTransaction dependentTransaction, int clientId, int index)
{
using (dependentTransaction)
{
//using (var transaction = CreateTransaction(dependentTransaction))
{
await using (var scope = serviceProvider.CreateAsyncScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AsyncDbContext>();
await Task.Delay(_randomer.Next(100, 250));
var client = await dbContext.Clients.AsNoTracking().Where(cl => cl.Id == clientId).SingleOrDefaultAsync();
await Console.Out.WriteLineAsync($"Read: {index} -> {client?.Id.ToString() ?? "!! NOT FOUND !!"}");
//transaction.Complete();
}
}
dependentTransaction.Complete();
}
}
主要问题是我需要并行读取数据,这只是主要的、更大的项目的一个简单示例,目标是相同的(它只是使用并行执行的插件,每个插件都通过 DI 实例化
DbContext
).
正如我所提到的,当使用
Sql Server
时我得到一个异常,但是当使用 Npgsql
时,查询有时返回正确的结果,有时返回 !! NOT FOUND !!
。后者让人相信在查询运行时事务以某种方式“回滚”。
那么我们实际上如何在所有这些
DbContext
实例之间共享连接,这样事务就不会升级为 DTC?