• 结合Command以AOP方式实现事务


    nuget包

    1. Autofac
    2. Autofac.Extensions.DependencyInjection
    3. MediatR
    4. 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; }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、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;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    三、TransactionBehaviour事务类

    这里类似于AOP编程,在进行注册后,能都在每个CommandHandler前后进行处理,这里是将每个handler包裹在一个事务里,来保证数据的一致,完整。

    //再MediatR 10版本后就需要规定继承 where TRequest : IRequest
    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;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、控制器

       [HttpPost]
            public async Task<int> Add([FromBody] CreateStudentCommand student)
            {
                return await _mediator.Send(student); 
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    五、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<,>));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    六 、MediatR模块注入

      public void ConfigureServices(IServiceCollection services)
            {
                //注入mediatR
                services.AddMediatR(typeof(Startup));
            }
            //MediatR的注册模块通过Autofac技术来进行批量注册
             public virtual void ConfigureContainer(ContainerBuilder builder)
            {
                builder.RegisterModule(new MediatorModule());
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    七、Startup设置Autofac工厂

     public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        //更改为默认使用Autofac
                    }).UseServiceProviderFactory(new AutofacServiceProviderFactory());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    八、演示效果

    1.在报错后保存,事务失败回退

    1. 添加数据
      在这里插入图片描述
    2. 事务失败,回退数据
      自定义报错信息
      在这里插入图片描述
    3. 结果数据无变化
      在这里插入图片描述

    2. 注释抛出异常代码,保存数据成功

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    导航栏参考代码
    介词练习题
    Searching for MobileNetV3翻译
    FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
    从0开始学习JavaScript--JavaScript DOM操作与事件处理
    SpringBoot整合Mybatis(使用c3p0数据源)
    一文打尽知识图谱(超级干货,建议收藏!)
    vue和react的生命周期
    选错毕业第一份工作,我白干半年
    csdn最牛最全的使用python自动发送邮件
  • 原文地址:https://blog.csdn.net/wsnbbdbbdbbdbb/article/details/126170955