Как сделать транзакцию EF с помощью шаблона CQRS mediatr в .NET?

#c# #entity-framework #cqrs #mediatr

Вопрос:

Предположим, у меня есть 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;
        }
    }
 

Поскольку я использую два репозитория, может случиться так, что в этой строке

ожидание _продукткатегориярепозиционная.addAsync(@Категория продукта);

что-то не получается, поэтому мне нужно выполнить откат всей транзакции. Поскольку я новичок в CQRS mediatr, у кого-нибудь есть идея и лучший подход для этого?

Комментарии:

1. Ссылаются ли оба репозитория на одну и ту же базу данных, используя одну и ту же строку подключения? Если нет, то вы весело проведете время , координируя распределенную транзакцию , в зависимости от операционной системы, базы данных и платформы

Ответ №1:

Если все это находится в пределах одного обработчика. Самым простым решением будет ввести 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() возникнет исключение, транзакция будет удалена и произойдет откат.