在以前的文章里面,我们介绍了 ABP vNext 在 DDD 模块定义了仓储的接口定义和基本实现。本章将会介绍,ABP vNext 是如何将 EntityFramework Core 框架跟仓储进行深度集成。
ABP vNext 在集成 EF Core 的时候,不只是简单地实现了仓储模式,除开仓储以外,还提供了一系列的基础设施,如领域事件的发布,数据过滤器的实现。
二、源码分析EntityFrameworkCore 相关的模块基本就下面几个,除了第一个是核心 EntityFrameworkCore 模块以外,其他几个都是封装的 EntityFrameworkCore Provider,方便各种数据库进行集成。
2.1 EF Core 模块集成与初始化首先从 Volo.Abp.EntityFrameworkCore 的 AbpEntityFrameworkCoreModule 开始分析,该模块只重写了 ConfigureServices() 方法,在内部也只有两句代码。
public override void ConfigureServices(ServiceConfigurationContext context) { // 调用 AbpDbContextOptions 的预配置方法,为了解决下面的问题。 // https://stackoverflow.com/questions/55369146/eager-loading-include-with-using-uselazyloadingproxies Configure<AbpDbContextOptions>(options => { options.PreConfigure(abpDbContextConfigurationContext => { abpDbContextConfigurationContext.DbContextOptions .ConfigureWarnings(warnings => { warnings.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning); }); }); }); // 注册 IDbContextProvider 组件。 context.Services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>)); }首先看第一句代码,它在内部会调用 AbpDbContextOptions 提供的 PreConfigure() 方法。这个方法逻辑很简单,会将传入的 Action<AbpDbContextConfigurationContext> 委托添加到一个 List<Action<AbpDbContextConfigurationContext>> 集合,并且在 DbContextOptionsFactory 工厂中使用。
第二局代码则比较简单,为 IDbContextProvider<> 类型注入默认实现 UnitOfWorkDbContextProvider<>。
public class AbpDbContextOptions { internal List<Action<AbpDbContextConfigurationContext>> DefaultPreConfigureActions { get; set; } // ... public void PreConfigure([NotNull] Action<AbpDbContextConfigurationContext> action) { Check.NotNull(action, nameof(action)); DefaultPreConfigureActions.Add(action); } // ... }从上面的代码可以看出来,这个 AbpDbContextConfigurationContext 就是一个配置上下文,用于 ABP vNext 框架在初始化的时候进行各种配置。
2.1.1 EF Core Provider 的集成在翻阅 AbpDbContextOptions 代码的时候,我发现除了预配置方法,它还提供了一个 Configure([NotNull] Action<AbpDbContextConfigurationContext> action) 方法,以及它的泛型重载 Configure<TDbContext>([NotNull] Action<AbpDbContextConfigurationContext<TDbContext>> action),它们的内部实现与预配置类似。
这两个方法在 ABP vNext 框架内部的应用,主要在各个 EF Provider 模块当中有体现。
这里我以 Volo.Abp.EntityFrameworkCore.PostgreSql 模块作为例子,在项目内部只有两个扩展方法的定义类。在 AbpDbContextOptionsPostgreSqlExtensions 当中,就使用到了 Configure() 方法。
public static void UsePostgreSql( [NotNull] this AbpDbContextOptions options, [CanBeNull] Action<NpgsqlDbContextOptionsBuilder> postgreSqlOptionsAction = null) { options.Configure(context => { // 这里的 context 类型是 AbpDbContextConfigurationContext。 context.UsePostgreSql(postgreSqlOptionsAction); }); }上面代码中的 UsePostgreSql() 方法很明显不是 EF Core Provider 所定义的扩展方法,跳转到具体实现,发现就是一层简单的封装。由于 AbpDbContextConfigurationContext 内部提供了 DbContextOptionsBuilder ,所以直接使用这个 DbContextOptionsBuilder 调用提供的扩展方法即可。
public static class AbpDbContextConfigurationContextPostgreSqlExtensions { public static DbContextOptionsBuilder UsePostgreSql( [NotNull] this AbpDbContextConfigurationContext context, [CanBeNull] Action<NpgsqlDbContextOptionsBuilder> postgreSqlOptionsAction = null) { if (context.ExistingConnection != null) { return context.DbContextOptions.UseNpgsql(context.ExistingConnection, postgreSqlOptionsAction); } else { return context.DbContextOptions.UseNpgsql(context.ConnectionString, postgreSqlOptionsAction); } } } 2.1.2 数据库上下文的配置工厂