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,可以使用它与主流的数据库提供商 ...
随机推荐
- QT XML文档的解析 QXmlStreamReader, DOM,SAX 三种解析方法 简单示例
0. xml文档如下 <?xml version="1.0"?> <bookindex> <entry term="sidebearings ...
- Linux嵌入式 -- 内核 - 系统调用
1. 系统调用 定义 Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用.用户可以通过系统调用命令在自己的应用程序中调用它们. 系统调用和普通的函数调用非常相似,区别仅仅在于,系统调 ...
- vue v-on监听事件
在html或jsp页面中我们总能碰到监听DOM事件来触发javaScript代码,下面我们就简单聊聊Vue.js中的监听事件是怎么处理的. 在vue.js中监听事件是通过v-on指令来实现的,先看一下 ...
- Lightoj 1370 素数打表 +二分
1370 - Bi-shoe and Phi-shoe PDF (English) Statistics Time Limit: 2 second(s) Memory Limit: 32 MB ...
- 天地图OGC WMTS服务规则
图层名称 服务地址 投影类型 矢量底图 http://t0.tianditu.gov.cn/vec_c/wmts?tk=您的密钥 经纬度投影 http://t0.tianditu.gov.cn/vec ...
- json数据转换异常:net.sf.json.JSONException: java.lang.reflect.InvocationTargetException
转:json数据转换异常:net.sf.json.JSONException: java.lang.reflect.InvocationTargetException 执行:JSONArray arr ...
- js字符串和数组的相互转化
一.数组转字符串 需要将数组元素用某个字符连接成字符串,示例代码如下: var a, b; a = new Array(0,1,2,3,4); b = a.join("-"); 二 ...
- selenium webdriver如何添加cookie
一. webdriver中常用的cookie方法 webdriver中提供了操作cookie的相关方法: get_cookies() 获得cookie信息 add_c ...
- javascript给输入框赋值的一个误区
一. 错误的示范 如下代码所示,如果需要用javascript获取id为username1, password1的输入框的值,将其写入id为username2, password2的输入框,那么红线区 ...
- poj2114树分治
题意:给你一棵树,每条边有权值,求有没有一条链使得权值和为k 题解:和上一题类似,依旧是树分治,只是我们储存结果的时候是判断加起来为k的点对数,刚开始本来想用map存答案,结果就t了,后来用了vect ...