[Abp vNext 源码分析] - 14. EntityFramework Core 的集成 (2)

无论是 PreConfigure() 的委托集合,还是 Configure() 配置的委托,都会在 DbContextOptionsFactory 提供的 Create<TDbContext>(IServiceProvider serviceProvider) 方法中被使用。该方法的作用只有一个,执行框架的配置方法,然后生成数据库上下文的配置对象。

internal static class DbContextOptionsFactory { public static DbContextOptions<TDbContext> Create<TDbContext>(IServiceProvider serviceProvider) where TDbContext : AbpDbContext<TDbContext> { // 获取一个 DbContextCreationContext 对象。 var creationContext = GetCreationContext<TDbContext>(serviceProvider); // 依据 creationContext 信息构造一个配置上下文。 var context = new AbpDbContextConfigurationContext<TDbContext>( creationContext.ConnectionString, serviceProvider, creationContext.ConnectionStringName, creationContext.ExistingConnection ); // 获取 AbpDbOptions 配置。 var options = GetDbContextOptions<TDbContext>(serviceProvider); // 从 Options 当中获取添加的 PreConfigure 与 Configure 委托,并执行。 PreConfigure(options, context); Configure(options, context); // return context.DbContextOptions.Options; } // ... }

首先我们来看看 GetCreationContext<TDbContext>() 方法是如何构造一个 DbContextCreationContext 对象的,它会优先从 Current 取得一个上下文对象,如果存在则直接返回,不存在则使用连接字符串等信息构建一个新的上下文对象。

private static DbContextCreationContext GetCreationContext<TDbContext>(IServiceProvider serviceProvider) where TDbContext : AbpDbContext<TDbContext> { // 优先从一个 AsyncLocal 当中获取。 var context = DbContextCreationContext.Current; if (context != null) { return context; } // 从 TDbContext 的 ConnectionStringName 特性获取连接字符串名称。 var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>(); // 使用 IConnectionStringResolver 根据指定的名称获得连接字符串。 var connectionString = serviceProvider.GetRequiredService<IConnectionStringResolver>().Resolve(connectionStringName); // 构造一个新的 DbContextCreationContext 对象。 return new DbContextCreationContext( connectionStringName, connectionString ); } 2.1.3 连接字符串解析器

与老版本的 ABP 一样,ABP vNext 将连接字符串解析的工作,抽象了一个解析器。连接字符串解析器默认有两种实现,适用于普通系统和多租户系统。

[Abp vNext 源码分析] - 14. EntityFramework Core 的集成

普通的解析器,名字叫做 DefaultConnectionStringResolver,它的连接字符串都是从 AbpDbConnectionOptions 当中获取的,而这个 Option 最终是从 IConfiguration 映射过来的,一般来说就是你 appsetting.json 文件当中的连接字符串配置。

多租户解析器 的实现叫做 MultiTenantConnectionStringResolver,它的内部核心逻辑就是获得到当前的租户,并查询租户所对应的连接字符串,这样就可以实现每个租户都拥有不同的数据库实例。

2.1.4 数据库上下文配置工厂的使用

回到最开始的地方,方法 Create<TDbContext>(IServiceProvider serviceProvider) 在什么地方会被使用呢?跳转到唯一的调用点是在 AbpEfCoreServiceCollectionExtensions 静态类的内部,它提供的 AddAbpDbContext<TDbContext>() 方法内部,就使用了 Create<TDbContext>() 作为 DbContextOptions<TDbContext> 的工厂方法。

public static class AbpEfCoreServiceCollectionExtensions { public static IServiceCollection AddAbpDbContext<TDbContext>( this IServiceCollection services, Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null) where TDbContext : AbpDbContext<TDbContext> { services.AddMemoryCache(); // 构造一个数据库注册配置对象。 var options = new AbpDbContextRegistrationOptions(typeof(TDbContext), services); // 回调传入的委托。 optionsBuilder?.Invoke(options); // 注入指定 TDbContext 的 DbOptions<TDbContext> ,将会使用 Create<TDbContext> 方法进行瞬时对象构造。 services.TryAddTransient(DbContextOptionsFactory.Create<TDbContext>); // 替换指定类型的 DbContext 为当前 TDbContext。 foreach (var dbContextType in options.ReplacedDbContextTypes) { services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext))); } // 构造 EF Core 仓储注册器,并添加仓储。 new EfCoreRepositoryRegistrar(options).AddRepositories(); return services; } } 2.2 仓储的注入与实现

关于仓储的注入,其实在之前的文章就有讲过,这里我就大概说一下情况。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjzfw.html