结合Command以AOP方式实现事务
nuget包
- Autofac
- Autofac.Extensions.DependencyInjection
- MediatR
- MediatR.Extensions.Microsoft.DependencyInjection
一、Command
public class CreateStudentCommand : IRequest<int>
{
public int ID { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
}
二、CommandHandler
public class CreateStudentCommandHandler : IRequestHandler<CreateStudentCommand, int>
{
private readonly StudentDbContext _studentDbContext;
private readonly IMediator _mediator;
public CreateStudentCommandHandler(StudentDbContext studentDbContext , IMediator mediator)
{
_studentDbContext = studentDbContext;
_mediator = mediator;
}
public async Task<int> Handle(CreateStudentCommand request, CancellationToken cancellationToken)
{
Student student = new Student
{
Name = "123",
Sex = "男",
Address = "huayuan",
Phone = "1768894221"
};
var entity = _studentDbContext.Student.Add(student);
await _studentDbContext.SaveChangesAsync();
//这里用来测试抛出异常情况,注释掉,是正常更改数据得方式
throw new UserFriendlyException("事务抛出异常");
return entity.Entity.ID;
}
三、TransactionBehaviour事务类
这里类似于AOP编程,在进行注册后,能都在每个CommandHandler前后进行处理,这里是将每个handler包裹在一个事务里,来保证数据的一致,完整。
//再MediatR 10版本后就需要规定继承 where TRequest : IRequest<TResponse>
public class TransactionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly ILogger<TransactionBehaviour<TRequest, TResponse>> _logger;
private readonly StudentDbContext _dbContext;
public TransactionBehaviour(ILogger<TransactionBehaviour<TRequest, TResponse>> logger,
StudentDbContext dbContext)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var response = default(TResponse);
var typeName = request.GetType().Name;
var strategy = _dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
//开启事务
using (var transaction = _dbContext.Database.BeginTransaction())
{
_logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request);
response = await next();
_logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName);
//提交事务
await transaction.CommitAsync();
}
});
return response;
}
四、控制器
[HttpPost]
public async Task<int> Add([FromBody] CreateStudentCommand student)
{
return await _mediator.Send(student);
}
五、MediatorModule注册MediatR
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly)
.AsImplementedInterfaces();
builder.Register<ServiceFactory>(context =>
{
var componentContext = context.Resolve<IComponentContext>();
return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; };
});
//事务处理类注册
builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>));
}
}
六 、MediatR模块注入
public void ConfigureServices(IServiceCollection services)
{
//注入mediatR
services.AddMediatR(typeof(Startup));
}
//MediatR的注册模块通过Autofac技术来进行批量注册
public virtual void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new MediatorModule());
}
七、Startup设置Autofac工厂
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
//更改为默认使用Autofac
}).UseServiceProviderFactory(new AutofacServiceProviderFactory());
八、演示效果
1.在报错后保存,事务失败回退
- 添加数据
- 事务失败,回退数据
自定义报错信息
- 结果数据无变化
2. 注释抛出异常代码,保存数据成功