Skip to content

如何使用仓储(二) 工作单元

alpha.yu edited this page Jul 5, 2022 · 17 revisions

前言

    如何使用仓储(一)-基础功能 介绍了仓储的总体架构与设计思路以及仓储基础功能的使用。本文主要介绍事务与工作单元。 Adnc.Infr.EfCore是EFCore仓储与工作单元的实现工程,集成了EFcore作为仓储的实现。

EFCore的事务

EFCore的事务分为以下三种

  • SaveChanges
  • DbContextTransaction
  • TransactionScope

EFCore默认情况下,SaveChanges是开启了事务的。然而基于以下原因ADNC关闭了SaveChanges自动事务。

  1. UpdateRangeAsync,DeleteRangeAsync 批量删除,批量更新方法不是走EfCore的原生方法,不受SaveChanges事务控制。
  2. Cap的事务也不受SaveChanges事务控制
  3. 原生SQL增删改查不受SaveChanges事务控制
  4. 最后一点就是与读写分离相关,请参考如何实现读写分离
public class AdncDbContext : DbContext
{
     public AdncDbContext()
     {
         Database.AutoTransactionsEnabled = false;
     }
}

在关闭SaveChanges自动事务后,ADNC通过DbContextTransaction统一控制事务。如果你的业务逻辑调用增/删/改的方法>=2次,就需要显示声明拦截器来开启事务控制。

主从表插入、主从表更新、批量新增、批量修改、批量删除操作都可以通过一个增/删/改的方法实现,并不需要显示开启事务。

Adnc.Infr.EfCore.MySQL实现了Adnc.Infra.RepositoryIUnitOfWork接口,通过servers.AddAdncInfraEfCoreMySql()统一注册了该接口。工作单元拦截器在Adnc.Application.Shared 工程的 Interceptors目录中,都已经实现好。我们只需要在使用的地方注入拦截器,然后显示声明。

如何使用

注册拦截器

services.AddAppliactionSerivcesWithInterceptors()该扩展方法中以及实现好,具体实现请参考源代码。

如果采用经典三层开发模式,在Application或者Repository层注册都可以。 如果采用DDD开发模式,则只能在Application层注册。

在经典三层开发模式中,ADNC是在Application层注册的。Adnc是在Repository层注册的,主要还是基于锁定更少的sql以及灵活控制读写分离的目的。如果在Repository层使用工作单元来控制事务,意味着你需要多写一些一点代码。 对于经典三层开发模式来说,在Application或者Repository层注册,都没有问题,取决于你自己的考虑。

protected virtual void AddAppliactionSerivcesWithInterceptor(Action<IServiceCollection> action = null)
{
            Services.Add(new ServiceDescriptor(implType, implType, lifetime));

            var serviceDescriptor = new ServiceDescriptor(serviceType, provider =>
            {
                var interceptors = DefaultInterceptorTypes.ConvertAll(interceptorType => provider.GetService(interceptorType) as IInterceptor).ToArray();
                var target = provider.GetService(implType);
                var interfaceToProxy = serviceType;
                var proxy = CastleProxyGenerator.CreateInterfaceProxyWithTarget(interfaceToProxy, target, interceptors);
                return proxy;
            }, lifetime);
            Services.Add(serviceDescriptor);
}

显示声明开启

统一在Adnc.Xxx.Application.Contracts层服务接口声明也就是在接口上声明。

//本地事务
//如:https://github.com/AlphaYu/Adnc/blob/master/src/ServerApi/Services/Adnc.Usr/Adnc.Usr.Application.Contracts/Services/IRoleAppService.cs
[UnitOfWork]
Task<AppSrvResult> UpdateAsync(long id, DeptUpdationDto input);

//CAP事务/分布式事务
//如:https://github.com/AlphaYu/Adnc/blob/master/src/ServerApi/Services/Adnc.Cus/Adnc.Cus.Application.Contracts/Services/ICustomerAppService.cs
[UnitOfWork(SharedToCap = true)]
Task<AppSrvResult> ProcessPayingAsync(long transactionLogId, long customerId, decimal amount);

自由调用开启

如果你不喜欢拦截器处理事务或者拦截器处理事务不能满足需求,那么你也可以自由开启。

public class xxxAppService
{
    private IUnitOfWork _uow;
    public xxxAppService(IUnitOfWork uow) _uow=>uow;
    public DemoMethod()
    {
        try
        {
            _uow.BeginTransaction(distributed:false);
            //操作1
            //操作2
            //操作3
            //操作N
            _unitOfWork.Commit();
        }
        catch (Exception ex)
        {
            _unitOfWork.Rollback();
        }
        finally
        {
            _unitOfWork.Dispose();
        }  
    }
}

WELL DONE
全文完