如何在工作单元事务范围内启用重试失败?



假设我有以下UnitOfWork可以注入到处理程序中并用于在TransactionScope中执行数据库操作:

internal sealed class UnitOfWork : IUnitOfWork
{
private readonly TransactionScope _transactionScope;
public UnitOfWork(IOptions<UnitOfWorkOptions> options)
{
EnsureArg.IsNotNull(options, nameof(options));
_transactionScope = new TransactionScope(
TransactionScopeOption.RequiresNew,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.DefaultTimeout,
},
TransactionScopeAsyncFlowOption.Enabled);
}
Task CompleteAsync(CancellationToken cancellationToken)
{
_transactionScope.Complete();
Dispose(true);
return Task.CompletedTask;
}
void IDisposable.Dispose()
=> Dispose(true);
private void Dispose(bool disposing)
{
_transactionScope.Dispose();
}
}

在我的处理程序中:

public MyHandler(IUnitOfWork unitOfWork, IRepository repository)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public async Task HandleAsync(CancellationToken cancellationToken)
{
var entity = await _repository.GetItem(cancellationToken);
await _unitOfWork.CompleteAsync(cancellationToken);
}

使用EnableRetryOnFailure启用 ef core 的连接弹性后,每当我的工作单元内发生任何事务时,我都会收到这个烦人的错误:

System.InvalidOperationException:"配置的执行策略'SqlServerRetryingExecutionStrategy'不支持用户启动的事务。使用"DbContext.Database.CreateExecutionStrategy()"返回的执行策略将事务中的所有操作作为可重试单元执行。

所以我注入了我的DbContext并尝试将我的数据库代码传递到CompleteAsync中以将其包装在执行策略周围,但相同的错误仍然存在:

Task CompleteAsync(Func<Task> action, CancellationToken cancellationToken)
{
var strategy = _dbContext.Database.CreateExecutionStrategy();
return strategy.ExecuteAsync(
async () =>
{
await action();
_transactionScope.Complete();
Dispose(true); 
return Task.CompletedTask;
});
}

处理器:

public async Task HandleAsync(CancellationToken cancellationToken)
{
var task = new Func<Task>(async () =>
{
var entity = await _repository.GetItem(cancellationToken);
});
await _unitOfWork.CompleteAsync(task, cancellationToken);
}

我也尝试简单地将_transactionScope.Complete();包装在执行策略中,但毫不奇怪,它没有帮助

有谁知道如何实现这一目标?

我让它像这样工作

Task CompleteAsync(Func<Task> action, CancellationToken cancellationToken)
{
var strategy = _dbContext.Database.CreateExecutionStrategy();
return strategy.ExecuteAsync(
async () =>
{
using (var transaction = new TransactionScope())
{
await action();
transaction.Complete();
Dispose(true); 
return Task.CompletedTask;
}
});
}

因此,它不是在构造函数中初始化TransactionScope,而是在最后一刻创建,并包装在执行策略中

他们确实在文档中这样做了:

using (var context1 = new BloggingContext())
{
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
var strategy = context1.Database.CreateExecutionStrategy();
strategy.Execute(
() =>
{
using (var context2 = new BloggingContext())
{
using (var transaction = new TransactionScope())
{
context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context2.SaveChanges();
context1.SaveChanges();
transaction.Complete();
}
}
});
}

最新更新