简介

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

例如:

  1. public class InvoiceAppService:IITransientDependency
  2. {
  3. private readonly IRepository<Invoice> _InvoiceRepository;
  4. public InvoiceAppService(IRepository<Invoice> InvoiceRepository)
  5. {
  6. _InvoiceRepository=InvoiceRepository;
  7. }
  8. public void Invoice(xxx)
  9. {
  10. InvoiceRepository.Insert(xxx);
  11. }
  12. }

通用仓储的定义与实现

ABP仓储定义如下

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

通用仓储的定义

通用仓储是由IRepository定义的,仅仅是起到了一个标识的作用

public interface IRepository : ITransientDependency{}

真正定义了仓储定义的是在IRepositoryOfTEntityAndTPrimaryKey

  1. public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
  2. {
  3. // 增删改查方法定义.
  4. }

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

通用仓储的实现

在 Abp 库里面,有一个默认的抽象基类实现了仓储接口,这个基类内部主要注入了 IUnitOfWorkManager 用来控制事务,还有 IIocResolver 用来解析 Ioc 容器内部注册的组件

本身在这个抽象仓储类里面没有什么实质性的东西,它只是之前 IRepository 的简单实现,在 EfCoreRepositoryBase 类当中则才是具体调用 EF Core API 的实现

  1. public class EfCoreRepositoryBase<TDbContext, TEntity, TPrimaryKey> :
  2. AbpRepositoryBase<TEntity, TPrimaryKey>,
  3. ISupportsExplicitLoading<TEntity, TPrimaryKey>,
  4. IRepositoryWithDbContext
  5. where TEntity : class, IEntity<TPrimaryKey>
  6. where TDbContext : DbContext
  7. {
  8. // 获取ef上下文对象
  9. public virtual TDbContext Context => _dbContextProvider.GetDbContext(MultiTenancySide);
  10. // 实体表
  11. public virtual DbSet<TEntity> Table => Context.Set<TEntity>();
  12. // 数据库事务
  13. public virtual DbTransaction Transaction
  14. {
  15. get
  16. {
  17. return (DbTransaction) TransactionProvider?.GetActiveTransaction(new ActiveTransactionProviderArgs
  18. {
  19. {"ContextType", typeof(TDbContext) },
  20. {"MultiTenancySide", MultiTenancySide }
  21. });
  22. }
  23. }
  24. // 数据库连接
  25. public virtual DbConnection Connection
  26. {
  27. get
  28. {
  29. var connection = Context.Database.GetDbConnection();
  30. if (connection.State != ConnectionState.Open)
  31. {
  32. connection.Open();
  33. }
  34. return connection;
  35. }
  36. }
  37. // 事务提供者,用于提供激活的事务
  38. public IActiveTransactionProvider TransactionProvider { private get; set; }
  39. // 上下文提供器
  40. private readonly IDbContextProvider<TDbContext> _dbContextProvider;
  41. // ctor
  42. public EfCoreRepositoryBase(IDbContextProvider<TDbContext> dbContextProvider)
  43. {
  44. _dbContextProvider = dbContextProvider;
  45. }
  46. // 其余crud方法
  47. }

通用仓储的注入

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

  1. public override void Initialize()
  2. {
  3. IocManager.RegisterAssemblyByConvention(typeof(AbpEntityFrameworkCoreModule).GetAssembly());
  4. IocManager.IocContainer.Register(
  5. Component.For(typeof(IDbContextProvider<>))
  6. .ImplementedBy(typeof(UnitOfWorkDbContextProvider<>))
  7. .LifestyleTransient()
  8. );
  9. RegisterGenericRepositoriesAndMatchDbContexes();
  10. }
  11. private void RegisterGenericRepositoriesAndMatchDbContexes()
  12. {
  13. // 获取所有ef上下文类型
  14. var dbContextTypes =
  15. _typeFinder.Find(type =>
  16. {
  17. var typeInfo = type.GetTypeInfo();
  18. return typeInfo.IsPublic &&
  19. !typeInfo.IsAbstract &&
  20. typeInfo.IsClass &&
  21. typeof(AbpDbContext).IsAssignableFrom(type);
  22. });
  23. if (dbContextTypes.IsNullOrEmpty())
  24. {
  25. Logger.Warn("No class found derived from AbpDbContext.");
  26. return;
  27. }
  28. // 创建ioc容器作用域
  29. using (IScopedIocResolver scope = IocManager.CreateScope())
  30. {
  31. // 遍历上下文
  32. foreach (var dbContextType in dbContextTypes)
  33. {
  34. Logger.Debug("Registering DbContext: " + dbContextType.AssemblyQualifiedName);
  35. // 为上下文每个实体注册仓储
  36. scope.Resolve<IEfGenericRepositoryRegistrar>().RegisterForDbContext(dbContextType, IocManager, EfCoreAutoRepositoryTypes.Default);
  37. // 为自定义的 上下文注册仓储
  38. IocManager.IocContainer.Register(
  39. Component.For<ISecondaryOrmRegistrar>()
  40. .Named(Guid.NewGuid().ToString("N"))
  41. .Instance(new EfCoreBasedSecondaryOrmRegistrar(dbContextType, scope.Resolve<IDbContextEntityFinder>()))
  42. .LifestyleTransient()
  43. );
  44. }
  45. //
  46. scope.Resolve<IDbContextTypeMatcher>().Populate(dbContextTypes);
  47. }
  48. }

下面看看是怎么注册的吧

  1. public void RegisterForDbContext(
  2. Type dbContextType,
  3. IIocManager iocManager,
  4. AutoRepositoryTypesAttribute defaultAutoRepositoryTypesAttribute)
  5. {
  6. var autoRepositoryAttr = dbContextType.GetTypeInfo().GetSingleAttributeOrNull<AutoRepositoryTypesAttribute>() ?? defaultAutoRepositoryTypesAttribute;
  7. RegisterForDbContext(
  8. dbContextType,
  9. iocManager,
  10. autoRepositoryAttr.RepositoryInterface,
  11. autoRepositoryAttr.RepositoryInterfaceWithPrimaryKey,
  12. autoRepositoryAttr.RepositoryImplementation,
  13. autoRepositoryAttr.RepositoryImplementationWithPrimaryKey
  14. );
  15. if (autoRepositoryAttr.WithDefaultRepositoryInterfaces)
  16. {
  17. RegisterForDbContext(
  18. dbContextType,
  19. iocManager,
  20. defaultAutoRepositoryTypesAttribute.RepositoryInterface,
  21. defaultAutoRepositoryTypesAttribute.RepositoryInterfaceWithPrimaryKey,
  22. autoRepositoryAttr.RepositoryImplementation,
  23. autoRepositoryAttr.RepositoryImplementationWithPrimaryKey
  24. );
  25. }
  26. }
  27. private void RegisterForDbContext(
  28. Type dbContextType,
  29. IIocManager iocManager,
  30. Type repositoryInterface,
  31. Type repositoryInterfaceWithPrimaryKey,
  32. Type repositoryImplementation,
  33. Type repositoryImplementationWithPrimaryKey)
  34. {
  35. // 遍历所有上下文类型
  36. foreach (var entityTypeInfo in _dbContextEntityFinder.GetEntityTypeInfos(dbContextType))
  37. {
  38. // 获取主键的类型
  39. var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityTypeInfo.EntityType);
  40. // 主键是int类型
  41. if (primaryKeyType == typeof(int))
  42. {
  43. // 根据实体类型动态构建一个泛型类型 例如 IRepository<Book>
  44. var genericRepositoryType = repositoryInterface.MakeGenericType(entityTypeInfo.EntityType;
  45. // 确定IOC容器中没有注册过
  46. if (!iocManager.IsRegistered(genericRepositoryType))
  47. {
  48. // 构建仓储实现类型
  49. var implType = repositoryImplementation.GetGenericArguments().Length == 1
  50. ? repositoryImplementation.MakeGenericType(entityTypeInfo.EntityType)
  51. : repositoryImplementation.MakeGenericType(entityTypeInfo.DeclaringType,
  52. entityTypeInfo.EntityType);
  53. // 注册
  54. iocManager.IocContainer.Register(
  55. Component
  56. .For(genericRepositoryType)
  57. .ImplementedBy(implType)
  58. .Named(Guid.NewGuid().ToString("N"))
  59. .LifestyleTransient()
  60. );
  61. }
  62. }
  63. // 如果主键不是int类型 构建如下类型:IRepostory<entity,key>
  64. var genericRepositoryTypeWithPrimaryKey = repositoryInterfaceWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType,primaryKeyType);
  65. if (!iocManager.IsRegistered(genericRepositoryTypeWithPrimaryKey))
  66. { // 构建仓储实现类
  67. var implType = repositoryImplementationWithPrimaryKey.GetGenericArguments().Length == 2
  68. ? repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType, primaryKeyType)
  69. : repositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.DeclaringType, entityTypeInfo.EntityType, primaryKeyType);
  70. // 注册
  71. iocManager.IocContainer.Register(
  72. Component
  73. .For(genericRepositoryTypeWithPrimaryKey)
  74. .ImplementedBy(implType)
  75. .Named(Guid.NewGuid().ToString("N"))
  76. .LifestyleTransient()
  77. );
  78. }
  79. }
  80. }

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. SGU 106 The equation 扩展欧几里德

    106. The equation time limit per test: 0.25 sec. memory limit per test: 4096 KB There is an equation ...

  2. CSS之动态相册

    注释还是比较详细的,所以我就不一一再说明了.--->运行效果图<--- (点击之前温馨提示一下:此picture是我自己的照片,本来不想拿出来的,后来觉得反正不会有很多人看到而且也没人认识 ...

  3. php 策略模式案例

    策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境. eg:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告.在传统的代码中,都是在 ...

  4. 分享知识-快乐自己:Java中的经典算法之冒泡排序(Bubble Sort)

    原理:比较两个相邻的元素,将值大的元素交换至右端. 思路:依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后.然后比较第2个数和第3个数,将 ...

  5. android根据原图片的路径得到该图片的缩略图

    /** * 根据图片的路径得到该图片在表中的ID * @param cr * @param fileName * @return */ public static String getImageIdF ...

  6. JDBC操作简单实用了IOUtils

    package cn.itcast.demo4; import java.io.FileInputStream; import java.io.FileOutputStream; import jav ...

  7. BEC listen and translation exercise 13

    The old lady sits on a mobile chair every morning. He got a large fortune when his father died, but ...

  8. (转)轻量级C语言实现的minixml解析库入门教程

    svn上的minixml源码下载:svn co http://svn.msweet.org/mxml/tags/release-2.7/ 按照下载回来的源代码进行编译和安装.本教程只针对新手做一个引导 ...

  9. NAT路由器打洞原理

    什么是打洞,为什么要打洞 由于Internet的快速发展 IPV4地址不够用,不能每个主机分到一个公网IP 所以使用NAT地址转换. 下面是我在网上找到的一副图 一般来说都是由私网内主机(例如上图中“ ...

  10. mysql之 共享表空间与独立表空间、frm,MYD,MYI.idb,par文件说明

    一.共享表空间与独立表空间MySQL5.5默认是共享表空间 ,5.6中,默认是独立表空间. 共享表空间:ibdata1是InnoDB的共享表空间,默认配置是把全部表空间存放到ibdata1中,因此而造 ...