ABP仓储

Posted zzqvq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP仓储相关的知识,希望对你有一定的参考价值。

简介

我们都知道ABP已经实现了仓储模式,支持EF core 和dapper 进行数据库的连接和管理,可以很方便的注入仓储来操作你的数据,不需要自己单独定义一个仓储来实现,通用的仓储实现了通用的crud接口和一些常用的方法
例如:

public class InvoiceAppService:IITransientDependency
{
    private readonly IRepository<Invoice> _InvoiceRepository;
    public InvoiceAppService(IRepository<Invoice> InvoiceRepository)
    {
        _InvoiceRepository=InvoiceRepository;
    }
    public void Invoice(xxx)
    {
        InvoiceRepository.Insert(xxx);
    }
}

通用仓储的定义与实现

ABP仓储定义如下

  • AbpRepositoryBase 仓储基类
  • AutoRepositoryTypesAttribute 自动构建仓储,用于实体标记
  • IRepository 仓储接口基本的定义
  • IRepositoryOfTEntity 仓储接口定义,默认为int类型
  • IRepositoryOfEntityAndTPrimaryKey 仓储接口定义,主键与实体类型由用户定义
  • ISupportsExplicitLoading 显示加载
  • RepositoryExtensions 仓储相关的扩展方法

    通用仓储的定义

    通用仓储是由IRepository定义的,仅仅是起到了一个标识的作用
    public interface IRepository : ITransientDependency{}
    真正定义了仓储定义的是在IRepositoryOfTEntityAndTPrimaryKey
public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
{
    //  增删改查方法定义.
}

可以看到它定义了两个泛型 TEntity 与 TPrimaryKey,表示了.实体与实体对应的主键标识

通用仓储的实现

在 Abp 库里面,有一个默认的抽象基类实现了仓储接口,这个基类内部主要注入了 IUnitOfWorkManager 用来控制事务,还有 IIocResolver 用来解析 Ioc 容器内部注册的组件
本身在这个抽象仓储类里面没有什么实质性的东西,它只是之前 IRepository 的简单实现,在 EfCoreRepositoryBase 类当中则才是具体调用 EF Core API 的实现

public class EfCoreRepositoryBase<TDbContext, TEntity, TPrimaryKey> : 
    AbpRepositoryBase<TEntity, TPrimaryKey>,
    ISupportsExplicitLoading<TEntity, TPrimaryKey>,
    IRepositoryWithDbContext
    
    where TEntity : class, IEntity<TPrimaryKey>
    where TDbContext : DbContext
{
    //  获取ef上下文对象
    public virtual TDbContext Context => _dbContextProvider.GetDbContext(MultiTenancySide);

    //  实体表
    public virtual DbSet<TEntity> Table => Context.Set<TEntity>();

    //  数据库事务
    public virtual DbTransaction Transaction
    {
        get
        {
            return (DbTransaction) TransactionProvider?.GetActiveTransaction(new ActiveTransactionProviderArgs
            {
                {"ContextType", typeof(TDbContext) },
                {"MultiTenancySide", MultiTenancySide }
            });
        }
    }
    //  数据库连接
    public virtual DbConnection Connection
    {
        get
        {
            var connection = Context.Database.GetDbConnection();

            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }

            return connection;
        }
    }
    // 事务提供者,用于提供激活的事务
    public IActiveTransactionProvider TransactionProvider { private get; set; }
    //  上下文提供器
    private readonly IDbContextProvider<TDbContext> _dbContextProvider;

    // ctor
    public EfCoreRepositoryBase(IDbContextProvider<TDbContext> dbContextProvider)
    {
        _dbContextProvider = dbContextProvider;
    }
    //  其余crud方法
}

通用仓储的注入

仓储的注入操作发生在 AbpEntityFrameworkCoreModule 模块执行 Initialize() 方法的时候,在 Initialize() 方法内部调用了 RegisterGenericRepositoriesAndMatchDbContexes() 方法,其定义如下:

public override void Initialize()
{
    IocManager.RegisterAssemblyByConvention(typeof(AbpEntityFrameworkCoreModule).GetAssembly());

    IocManager.IocContainer.Register(
        Component.For(typeof(IDbContextProvider<>))
            .ImplementedBy(typeof(UnitOfWorkDbContextProvider<>))
            .LifestyleTransient()
        );

    RegisterGenericRepositoriesAndMatchDbContexes();
}

private void RegisterGenericRepositoriesAndMatchDbContexes()
{
    // 获取所有ef上下文类型
    var dbContextTypes =
        _typeFinder.Find(type =>
        {
            var typeInfo = type.GetTypeInfo();
            return typeInfo.IsPublic &&
                    !typeInfo.IsAbstract &&
                    typeInfo.IsClass &&
                    typeof(AbpDbContext).IsAssignableFrom(type);
        });

    if (dbContextTypes.IsNullOrEmpty())
    {
        Logger.Warn("No class found derived from AbpDbContext.");
        return;
    }
    //  创建ioc容器作用域
    using (IScopedIocResolver scope = IocManager.CreateScope())
    {
        //  遍历上下文
        foreach (var dbContextType in dbContextTypes)
        {
            Logger.Debug("Registering DbContext: " + dbContextType.AssemblyQualifiedName);

            // 为上下文每个实体注册仓储
            scope.Resolve<IEfGenericRepositoryRegistrar>().RegisterForDbContext(dbContextType, IocManager, EfCoreAutoRepositoryTypes.Default);
            //  为自定义的 上下文注册仓储
            IocManager.IocContainer.Register(
                Component.For<ISecondaryOrmRegistrar>()
                    .Named(Guid.NewGuid().ToString("N"))
                    .Instance(new EfCoreBasedSecondaryOrmRegistrar(dbContextType, scope.Resolve<IDbContextEntityFinder>()))
                    .LifestyleTransient()
            );
        }
        //  
        scope.Resolve<IDbContextTypeMatcher>().Populate(dbContextTypes);
    }
}

下面看看是怎么注册的吧

public void RegisterForDbContext(
    Type dbContextType, 
    IIocManager iocManager, 
    AutoRepositoryTypesAttribute defaultAutoRepositoryTypesAttribute)
{
    var autoRepositoryAttr = dbContextType.GetTypeInfo().GetSingleAttributeOrNull<AutoRepositoryTypesAttribute>() ?? defaultAutoRepositoryTypesAttribute;

    RegisterForDbContext(
        dbContextType,
        iocManager,
        autoRepositoryAttr.RepositoryInterface,
        autoRepositoryAttr.RepositoryInterfaceWithPrimaryKey,
        autoRepositoryAttr.RepositoryImplementation,
        autoRepositoryAttr.RepositoryImplementationWithPrimaryKey
    );

    if (autoRepositoryAttr.WithDefaultRepositoryInterfaces)
    {
        RegisterForDbContext(
            dbContextType,
            iocManager,
            defaultAutoRepositoryTypesAttribute.RepositoryInterface,
            defaultAutoRepositoryTypesAttribute.RepositoryInterfaceWithPrimaryKey,
            autoRepositoryAttr.RepositoryImplementation,
            autoRepositoryAttr.RepositoryImplementationWithPrimaryKey
        );
    }
}

private void RegisterForDbContext(
    Type dbContextType, 
    IIocManager iocManager,
    Type repositoryInterface,
    Type repositoryInterfaceWithPrimaryKey,
    Type repositoryImplementation,
    Type repositoryImplementationWithPrimaryKey)
{
    //  遍历所有上下文类型
    foreach (var entityTypeInfo in _dbContextEntityFinder.GetEntityTypeInfos(dbContextType))
    {
        //  获取主键的类型
        var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityTypeInfo.EntityType);
        //  主键是int类型
        if (primaryKeyType == typeof(int))
        {
            //  根据实体类型动态构建一个泛型类型 例如 IRepository<Book>
            var genericRepositoryType = repositoryInterface.MakeGenericType(entityTypeInfo.EntityType;
            //  确定IOC容器中没有注册过
            if (!iocManager.IsRegistered(genericRepositoryType))
            {
                //  构建仓储实现类型
                var implType = repositoryImplementation.GetGenericArguments().Length == 1
                    ? repositoryImplementation.MakeGenericType(entityTypeInfo.EntityType)
                    : repositoryImplementation.MakeGenericType(entityTypeInfo.DeclaringType,
                        entityTypeInfo.EntityType);
                //  注册
                iocManager.IocContainer.Register(
                    Component
                        .For(genericRepositoryType)
                        .ImplementedBy(implType)
                        .Named(Guid.NewGuid().ToString("N"))
                        .LifestyleTransient()
                );
            }
        }
        //  如果主键不是int类型 构建如下类型:IRepostory<entity,key>
        var genericRepositoryTypeWithPrimaryKey = repositoryInterfaceWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType,primaryKeyType);
        if (!iocManager.IsRegistered(genericRepositoryTypeWithPrimaryKey))
        {   //  构建仓储实现类
            var implType = repositoryImplementationWithPrimaryKey.GetGenericArguments().Length == 2
                ? repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType, primaryKeyType)
                : repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.DeclaringType, entityTypeInfo.EntityType, primaryKeyType);
            //  注册
            iocManager.IocContainer.Register(
                Component
                    .For(genericRepositoryTypeWithPrimaryKey)
                    .ImplementedBy(implType)
                    .Named(Guid.NewGuid().ToString("N"))
                    .LifestyleTransient()
            );
        }
    }
}

以上是关于ABP仓储的主要内容,如果未能解决你的问题,请参考以下文章

abp(net core)+easyui+efcore实现仓储管理系统——菜单-下(十七)

ABP官方文档翻译 3.3 仓储

ABP官方文档翻译 3.3 仓储

abp(net core)+easyui+efcore实现仓储管理系统——模块管理升级之上(六十一)

ABP领域层-仓储

Abp 仓储的讨论