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仓储的更多相关文章
- 基于DDD的.NET开发框架 - ABP仓储实现
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- ABP 仓储VIEW实现
每个view实体加个ID 对应实体属性配置到View的列 View添加一列类似自增序列 ROW_NUMBER() OVER(ORDER BY getdate()) AS ID codefirst时 删 ...
- ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...
- ABP领域层——仓储(Repositories)
ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...
- 一步一步学习ABP项目系列文章目录
1.概述 基于DDD的.NET开发框架 - ABP初探 基于DDD的.NET开发框架 - ABP分层设计 基于DDD的.NET开发框架 - ABP模块设计 基于DDD的.NET开发框架 - ABP启动 ...
- 关于领域驱动设计(DDD)仓储的思考
为什么需要仓储呢?领域对象(一般是聚合根)的被创建出来后的到最后持久化到数据库都需要跟数据库打交道,这样我们就需要一个类似数据库访问层的东西来管理领域对象.那是不是我们就可以设计一个类似DAL层的东东 ...
- (DDD)仓储的思考
(DDD)仓储的思考 为什么需要仓储呢?领域对象(一般是聚合根)的被创建出来后的到最后持久化到数据库都需要跟数据库打交道,这样我们就需要一个类似数据库访问层的东西来管理领域对象.那是不是我们就可以设计 ...
- ABP官方文档翻译 3.5 规约
规约 介绍 示例 创建规范类 使用仓储规约 组合规约 讨论 什么时候使用? 什么时候不使用? 介绍 规约模式是一种特别的软件设计模式,通过使用布尔逻辑将业务规则链接起来重新调配业务规则.(维基百科). ...
- ABP框架之——数据访问基础架构(下)
大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. EF Core集成 EF Core是微软的ORM,可以使用它与主流的数据库提供商 ...
随机推荐
- elasticsearch中如何手动控制全文检索结果的精准度
1.为帖子数据增加标题字段 POST /forum/article/_bulk{ "update": { "_id": "1"} }{ &q ...
- nova Evacuate
作用:当一个 node down 掉后,在新的 node 上根据其 DB 中保存的信息重新 build down node 上虚机.这个往往在虚机 HA 方案中用到.它尽可能地将原来的虚机在新的主机上 ...
- Spring Boot入门——thymeleaf模板使用
使用步骤 1.在pom.xml中引入thymeleaf <!-- thymeleaf插件 --> <dependency> <groupId>org.springf ...
- CentOS下用于查看系统当前登录用户信息的4种方法
作为系统管理员,你可能经常会(在某个时候)需要查看系统中有哪些用户正在活动.有些时候,你甚至需要知道他(她)们正在做什么.本文为我们总结了4种查看系统用户信息(通过编号(ID))的方法. 1. 使用w ...
- 理解collate Chinese_PRC_CI_AS
我们在create table时经常会碰到这样的语句,例如:password nvarchar(10)collate chinese_prc_ci_as null,那它到底是什么意思呢?不妨看看下面: ...
- 用node.js可以开启静态服务 不需要借助apache 或者xampl
安装好了Node以及express,然后用express命令生成express架构, 目录结构下面有一个public页面, 把你的静态页面放到这个文件夹下, 通过npm start,开启服务就可以在浏 ...
- LeetCode OJ:Binary Tree Inorder Traversal(中序遍历二叉树)
Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...
- 剑指offer--13.二进制中1的个数
就是猜测试数据没有负数,哈哈 ----------------------------------------------------------------- 时间限制:1秒 空间限制:32768K ...
- pcre函数详解
PCRE是一个NFA正则引擎,不然不能提供完全与Perl一致的正则语法功能.但它同时也实现了DFA,只是满足数学意义上的正则. PCRE提供了19个接口函数,为了简单介绍,使用PCRE内带的测试程序( ...
- UVALive - 4126 Password Suspects (AC自动机+状压dp)
给你m个字符串,让你构造一个字符串,包含所有的m个子串,问有多少种构造方法.如果答案不超过42,则按字典序输出所有可行解. 由于m很小,所以可以考虑状压. 首先对全部m个子串构造出AC自动机,每个节点 ...