简介

我们都知道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仓储的更多相关文章

  1. 基于DDD的.NET开发框架 - ABP仓储实现

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  2. ABP 仓储VIEW实现

    每个view实体加个ID 对应实体属性配置到View的列 View添加一列类似自增序列 ROW_NUMBER() OVER(ORDER BY getdate()) AS ID codefirst时 删 ...

  3. ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...

  4. ABP领域层——仓储(Repositories)

    ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...

  5. 一步一步学习ABP项目系列文章目录

    1.概述 基于DDD的.NET开发框架 - ABP初探 基于DDD的.NET开发框架 - ABP分层设计 基于DDD的.NET开发框架 - ABP模块设计 基于DDD的.NET开发框架 - ABP启动 ...

  6. 关于领域驱动设计(DDD)仓储的思考

    为什么需要仓储呢?领域对象(一般是聚合根)的被创建出来后的到最后持久化到数据库都需要跟数据库打交道,这样我们就需要一个类似数据库访问层的东西来管理领域对象.那是不是我们就可以设计一个类似DAL层的东东 ...

  7. (DDD)仓储的思考

    (DDD)仓储的思考 为什么需要仓储呢?领域对象(一般是聚合根)的被创建出来后的到最后持久化到数据库都需要跟数据库打交道,这样我们就需要一个类似数据库访问层的东西来管理领域对象.那是不是我们就可以设计 ...

  8. ABP官方文档翻译 3.5 规约

    规约 介绍 示例 创建规范类 使用仓储规约 组合规约 讨论 什么时候使用? 什么时候不使用? 介绍 规约模式是一种特别的软件设计模式,通过使用布尔逻辑将业务规则链接起来重新调配业务规则.(维基百科). ...

  9. ABP框架之——数据访问基础架构(下)

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. EF Core集成 EF Core是微软的ORM,可以使用它与主流的数据库提供商 ...

随机推荐

  1. Mysql -- SQL常用命令实例

    sql: structured query language(结构化查询语言) 用户名和密码:root 创建一个名称为mydb1的数据库. create database mydb1; 查看所有数据库 ...

  2. 初探MyBatis之HelloWorld(三)

    三.用SQL映射语句用注解,dataSource用xml(不推荐). 综合上面两节(一个用xml,一个用annotation),发现一个好玩儿的,SQL映射用注解方式,然后还是得有两个xml配置文件. ...

  3. Spring初学之spring的事务管理注解

    spring的事务管理,本文的例子是:比如你需要网购一本书,卖书的那一方有库存量以及书的价格,你有账户余额.回想我们在编程中要实现买书这样的功能,由于你的账户表和书的库存量表肯定不是同一张数据库表,所 ...

  4. 智课雅思词汇---十九、前缀se是什么意思

    智课雅思词汇---十九.前缀se是什么意思 一.总结 一句话总结:前缀:se- 表示“分开, 离开, 区别开” 前缀:se- [词根含义]:分离 [同源单词]:secede, secession, s ...

  5. ActiveMQ消费者接收消息的过程

    [http://manzhizhen.iteye.com/blog/2094130] org.apache.activemq.ActiveMQMessageConsumer同步接收

  6. mac 不再接受预览版提示

    系统偏好设置 - App Store - 点“电脑已设置为接收预发布的软件更新种子”的“更改”按钮 - 点“不显示预发布更新”.

  7. vue组件父子组件传递引用类型数据

    今天在写分页功能时,发现父子组件传值时,子组件监听不到父组件中数据的变化,传递的是一个引用类型的数据 其原因是引用类型共用一个内存地址,父子组件用的是同一个对象,故子组件监听不到变化,此时就需要做一个 ...

  8. 【Prism】MEF版UIComposition

    引言 UIComposition原版Demo在PrismV5的解压包里面.原Demo用了.net4.5版本的DLL,我改成.net4.0的. RegionContext 这个Demo比之前那几个示例大 ...

  9. GEF入门实例_总结_04_Eclipse插件启动流程分析

    一.前言 本文承接上一节:GEF入门实例_总结_03_显示菜单和工具栏 注意到app目录下的6个类文件. 这6个文件对RCP应用程序而言非常重要,可能我们现在对这几个文件的理解还是云里雾里,这一节我们 ...

  10. 条款44:将与参数无关的代码剥离template

    使用template时,不小心的时候可能就会带来代码膨胀的问题: template<typename T, std::size_t n> class SquareMatrix{ publi ...