Mat*_*att 5 .net transactions .net-core
在我的.NET Core应用程序中,我有一个装饰器类,我希望通过在TransactionScope中包装数据库命令的执行来处理事务.不幸的是,似乎支持TransactionScope不会通过.NET Core 2的发布进入SqlConnection:https://github.com/dotnet/corefx/issues/19708:
在缺少TransactionScope的情况下,我不确定解决此问题的最佳方法.使用TransactionScope,我的事务装饰器如下所示:
public class TransactionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decorated;
//constructor
public void Handle(TCommand command)
{
using (var scope = new TransactionScope())
{
this.decorated.Handle(command);
scope.Complete();
}
}
}
Run Code Online (Sandbox Code Playgroud)
目前,ICommandHandler的每个实现都获取了我的DapperContext类的实例,并处理如下命令:
public void Handle(UpdateEntity command)
{
var sql = Resources.UpdateEntityPart1;
this.context.Execute(sql, new
{
id = command.Id;
});
var sql = Resources.UpdateEntityPart2;
//call Execute again
}
Run Code Online (Sandbox Code Playgroud)
DapperContext类有一个连接工厂,为每次调用Execute方法提供新的连接.因为命令处理程序可能必须为单个TCommand执行多个数据库写入,所以我需要能够在出现故障时回滚.必须在我创建连接的同时创建事务(在DapperContext中)意味着我无法保证跨连接的事务行为.
我考虑过的另一种选择似乎并不令人满意:
我的问题是:考虑到.NET Core中SqlConnection的当前限制,有没有办法在不使用TransactionScope的情况下编写事务装饰器?如果没有,那么下一个最好的解决方案是什么,并不会过于严重地违反单一责任原则?
解决方案可能是创建 aSqlTransaction
作为装饰器的一部分,并将其存储在某种ThreadLocal
或AsyncLocal
字段中,以便它可用于业务事务的其他部分,即使它没有显式传递。这实际上是TransactionScope
在幕后所做的事情(但更优雅)。
例如,看一下这个伪代码:
public class TransactionCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decorated;
private readonly AsyncLocal<SqlTransaction> transaction;
public void Handle(TCommand command)
{
transaction.Value = BeginTranscation();
try
{
this.decorated.Handle(command);
transaction.Value.Commit();
}
finally
{
transaction.Value.Dispose();
transaction.Value = null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过处理程序可以使用的抽象:
public interface ITransactionContainer
{
SqlTransaction CurrentTransaction { get; }
}
public void Handle(UpdateEntity command)
{
// Get current transaction
var transaction = this.transactionContainer.CurrentTransaction;
var sql = Resources.UpdateEntityPart1;
// Pass the transaction on to the Execute
// (or hide it inside the execute would be even better)
this.context.Execute(sql, transaction, new
{
id = command.Id;
});
var sql = Resources.UpdateEntityPart2;
//call Execute again
}
Run Code Online (Sandbox Code Playgroud)
的实现ITransactionContainer
可能如下所示:
public class AsyncTransactionContainer : ITransactionContainer
{
private readonly AsyncLocal<SqlTransaction> transaction;
public AsyncTransactionContainer(AsyncLocal<SqlTransaction> transaction)
{
this.transaction = transaction;
}
public SqlTransaction CurrentTransaction =>
this.transaction.Value
?? throw new InvalidOperationException("No transaction.");
}
Run Code Online (Sandbox Code Playgroud)
和 都AsyncTransactionContainer
依赖TransactionCommandHandlerDecorator
于AsyncLocal<SqlTransaction>
. 这应该是一个单例(同一个实例应该注入到两者中)。
归档时间: |
|
查看次数: |
1038 次 |
最近记录: |