GET*_*Tah 7 c# entity-framework-core
我们将大型 DbContext 拆分为较小的上下文,每个上下文负责一个小的域边界上下文。上下文保存操作由工作单元编排,如下所示。
该域有两个有界上下文:合作伙伴和员工。工作单元管理两个 DbContext,PartnerContext并且EmployeeContext. 我们在事务中运行所有保存操作以确保操作是原子的。
github上提供了该问题的简化版本
public class UnitOfWork {
public Task SaveChanges(){
// EmployeeContext begins a transaction and shares it with other contexts
var strategy = employeeContext.Database.CreateExecutionStrategy();
return strategy.ExecuteAsync(async () =>
{
await using var transaction = await employeeContext.Database.BeginTransactionAsync();
await partnerContext.Database.UseTransactionAsync(transaction.GetDbTransaction());
await partnerContext.SaveChangesAsync();
await employeeContext.SaveChangesAsync();
await transaction.CommitAsync();
});
}
}
Run Code Online (Sandbox Code Playgroud)
下面的代码工作正常。更改全部在一个事务中执行
var unitOfWork = new unitOfWork();
... perform updates to both contexts
await unitOfWork.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
但是,当尝试第二次保存更改时,以下代码会抛出异常。
var unitOfWork = new unitOfWork();
... perform updates to both contexts
await unitOfWork.SaveChanges(); <-- Work fine
... doing a bit more work
await unitOfWork.SaveChanges(); <-- Crashes
Run Code Online (Sandbox Code Playgroud)
上面的行失败并显示The connection is already in a transaction and cannot participate in another transaction.错误消息。
生成的 SQL 日志为:
**** The first save operation logs start here:
SET NOCOUNT ON;
INSERT INTO [Person] ([Discriminator], [ManagerId], [Name])
VALUES (@p0, @p1, @p2);
SELECT [Id]
FROM [Person]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
Microsoft.EntityFrameworkCore.Database.Transaction: Debug: Committing transaction.
Microsoft.EntityFrameworkCore.Database.Transaction: Debug: Disposing transaction.
**** The second save operation logs start here:
Microsoft.EntityFrameworkCore.Database.Connection: Debug: Opening connection to database 'EF_DDD' on server 'localhost'.
Microsoft.EntityFrameworkCore.Database.Connection: Debug: Opened connection to database 'EF_DDD' on server 'localhost'.
Microsoft.EntityFrameworkCore.Database.Transaction: Debug: Beginning transaction with isolation level 'Unspecified'.
Microsoft.EntityFrameworkCore.Database.Transaction: Debug: Began transaction with isolation level 'ReadCommitted'.
Microsoft.EntityFrameworkCore.Database.Transaction: Debug: Disposing transaction.
Run Code Online (Sandbox Code Playgroud)
unitOfWork.SaveChanges()尽管第一笔交易已提交并处理(正如您从上面的日志中看到的那样),有人知道第二笔抱怨开放交易背后的原因吗?
更新
我删除了所有异步代码和执行策略(重试)以缩小问题范围,代码现在如下所示:
static void Main(string[] args)
{
var employeeContext = new EmployeeContext(ConnectionString);
var partnersContext = new PartnersContext(employeeContext.Database.GetDbConnection());
var unitOfWork = new UnitOfWork();
unitOfWork.Update(employeeContext, partnersContext, 1);
unitOfWork.Update(employeeContext, partnersContext, 2);
}
public class UnitOfWork
{
public void Update(EmployeeContext employeeContext, PartnersContext partnerContext, int count)
{
partnerContext.Partners.Add(new Partner($"John Smith {count}"));
employeeContext.Persons.Add(new Person() { Name = $"Richard Keno {count}" });
using var trans = employeeContext.Database.BeginTransaction();
partnerContext.Database.UseTransaction(trans.GetDbTransaction());
partnerContext.SaveChanges();
employeeContext.SaveChanges();
trans.Commit();
}
}
Run Code Online (Sandbox Code Playgroud)
第一个调用完成,数据库已更新,但第二个调用失败并出现以下错误。
更新2
使用TransactionScope代替BeginTransaction似乎有效。以下代码有效并相应地更新数据库。
var strategy = employeeContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
partnerContext.Partners.Add(new Partner($"John Smith {count}"));
employeeContext.Persons.Add(new Person() { Name = $"Richard Keno {count}" });
await partnerContext.SaveChangesAsync();
await employeeContext.SaveChangesAsync();
scope.Complete();
});
Run Code Online (Sandbox Code Playgroud)
看起来您遇到了一些内部 EF Core 3 实现缺陷/错误,该缺陷/错误已在未来修复,因为该问题不会在目前最新的 EF Core 6.0 中重现(但会在 EF Core 3.1 中重现)。
问题在于共享底层数据库连接和数据库事务的清理。可以通过处理调用IDbContextTransaction返回的 EF Core 事务包装器 ( )来解决(这也有助于未来的 EF Core 版本) UseTransaction{Async},例如
using var trans2 = await partnerContext.Database.UseTransactionAsync(transaction.GetDbTransaction());
Run Code Online (Sandbox Code Playgroud)
或者
using var trans2 = partnerContext.Database.UseTransaction(trans.GetDbTransaction());
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3562 次 |
| 最近记录: |