首页 > 解决方案 > 如何在 .NET 中使用 CQRS 模式 mediatr 使 EF 事务化?

问题描述

假设我有CreateProductCommandHandler

public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid>
    {
        private readonly IProductRepository _productRepository;
        private readonly IProductCategoryRepository _productCategoryRepository;
        private readonly IMapper _mapper;
        private readonly IEmailService _emailService;

        public CreateProductCommandHandler(IEmailService emailService, IProductRepository productRepository, IMapper mapper, IProductCategoryRepository productCategoryRepository)
        {
            _mapper = mapper;
            _productRepository = productRepository;
            _emailService = emailService;
            _productCategoryRepository = productCategoryRepository;
        }

        public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
        {
            var validator = new CreateProductCommandValidator(_productRepository);
            var validationResult = await validator.ValidateAsync(request);

            if (validationResult.Errors.Count > 0)
                throw new Exceptions.ValidationException(validationResult);


            var @product = _mapper.Map<Product>(request);

            @product = await _productRepository.AddAsync(@product);


            var @productCategory = new ProductCategory();
            @productCategory.ProductID = @product.ProductID;
            foreach (var cat in request.listOfCategories)
            {
                @productCategory.CategoryID = cat.CategoryID;
                await _productCategoryRepository.AddAsync(@productCategory);
            }
            return @product.Uid;
        }
    }

当我使用两个存储库时,可能会出现在这一行

等待 _productCategoryRepository.AddAsync(@productCategory);

有些事情失败了,所以我需要对整个事务进行回滚。由于我是 CQRS mediatr 的新手,有人有想法和最好的方法来做到这一点吗?

标签: c#entity-frameworkcqrsmediatr

解决方案


如果这一切都在一个处理程序的范围内。最简单的解决方案是注入 DbContext 并创建一个包含所有存储库调用的事务,如下所示。

public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid>
{
    private readonly IProductRepository _productRepository;
    private readonly IProductCategoryRepository _productCategoryRepository;
    private readonly SomeDbContext _dbContext;
    private readonly IMapper _mapper;
    private readonly IEmailService _emailService;

    public CreateProductCommandHandler(IEmailService emailService, IProductRepository productRepository, IMapper mapper, IProductCategoryRepository productCategoryRepository, SomeDbContext dbContext)
    {
        _mapper = mapper;
        _productRepository = productRepository;
        _emailService = emailService;
        _productCategoryRepository = productCategoryRepository;
        _dbContext = dbContext;
    }

    public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
    {
        var validator = new CreateProductCommandValidator(_productRepository);
        var validationResult = await validator.ValidateAsync(request);

        if (validationResult.Errors.Count > 0)
            throw new Exceptions.ValidationException(validationResult);
        

        var @product = _mapper.Map<Product>(request);

        await using var transaction = await _dbContext.Database.BeginTransactionAsync();
        @product = await _productRepository.AddAsync(@product);


        var @productCategory = new ProductCategory();
        @productCategory.ProductID = @product.ProductID;
        foreach (var cat in request.listOfCategories)
        {
            @productCategory.CategoryID = cat.CategoryID;
            await _productCategoryRepository.AddAsync(@productCategory);
        }
        await transaction.CommitAsync();
        return @product.Uid;
    }
}

如果在您调用 CommitAsync() 之前发生异常,事务将被释放并发生回滚


推荐阅读