批量注册DbContext

1
2
3
4
5
services.AddAllDbContexts(ctx =>
{
string connStr = configuration.GetValue<string>("DefaultDB:ConnStr");
ctx.UseSqlServer(connStr);
}, assemblies);

AddAllDbContexts是自定义扩展方法,作用是自动注册所有DbContext 类型到依赖注入容器中。
configuration.GetValue<string>(“DefaultDB:ConnStr”)从配置系统中读取连接字符串,我是配置到环境变量。
assemblies 中所有实现了 DbContext 的类,并使用提供的委托配置它们。

AddAllDbContexts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static class EFCoreInitializerHelper
{
/// <summary>
/// 批量注册指定程序集中的所有 DbContext 类型到依赖注入容器中。
/// </summary>
/// <param name="services">依赖注入服务容器。</param>
/// <param name="builder">用于配置 DbContext 选项的委托。</param>
/// <param name="assemblies">包含 DbContext 类型的程序集集合。</param>
/// <returns>返回服务容器本身,便于链式调用。</returns>
public static IServiceCollection AddAllDbContexts(this IServiceCollection services, Action<DbContextOptionsBuilder> builder,
IEnumerable<Assembly> assemblies)
{
// 定义 AddDbContext 泛型扩展方法的参数类型数组
Type[] types = new Type[] { typeof(IServiceCollection), typeof(Action<DbContextOptionsBuilder>), typeof(ServiceLifetime), typeof(ServiceLifetime) };
// 通过反射获取 EntityFrameworkServiceCollectionExtensions.AddDbContext<TContext> 泛型方法信息
var methodAddDbContext = typeof(EntityFrameworkServiceCollectionExtensions)
.GetMethod(nameof(EntityFrameworkServiceCollectionExtensions.AddDbContext), 1, types);
// 遍历所有传入的程序集
foreach (var asmToLoad in assemblies)
{
// 获取程序集中的所有类型
Type[] typesInAsm = asmToLoad.GetTypes();
// 查找所有非抽象且继承自 DbContext 的类型
foreach (var dbCtxType in typesInAsm
.Where(t => !t.IsAbstract && typeof(DbContext).IsAssignableFrom(t)))
{
// 构造 AddDbContext<TContext> 泛型方法
var methodGenericAddDbContext = methodAddDbContext.MakeGenericMethod(dbCtxType);
// 调用 AddDbContext<TContext> 方法,将 DbContext 注册到服务容器
methodAddDbContext.Invoke(null, new object[]
{
services, // 依赖注入服务容器
builder, // DbContext 配置委托
ServiceLifetime.Scoped, // DbContext 生命周期为 Scoped
ServiceLifetime.Singleton// 选项生命周期为 Singleton
});
}
}
// 返回服务容器本身
return services;
}
}

你这段代码实现了一个 批量注册所有 DbContext 的工具方法 AddAllDbContexts,用于自动扫描并注册多个程序集中的所有 DbContext 类型,避免了手动一个一个写 services.AddDbContext&ltMyContext>()。

解析

反射查找 AddDbContext<TContext>() 方法

1
2
3
Type[] types = new Type[] { typeof(IServiceCollection), typeof(Action<DbContextOptionsBuilder>), typeof(ServiceLifetime), typeof(ServiceLifetime) };
var methodAddDbContext = typeof(EntityFrameworkServiceCollectionExtensions)
.GetMethod(nameof(EntityFrameworkServiceCollectionExtensions.AddDbContext), 1, types);

目标是找到 EF Core 中的泛型方法 AddDbContext<TContext>(this IServiceCollection, Action<DbContextOptionsBuilder>, ServiceLifetime, ServiceLifetime)
GetMethod 的参数 1 表示这个方法有 1 个泛型类型参数。

方法签名中参数为:

1
2
3
4
5
6
AddDbContext<TContext>(
this IServiceCollection services,
Action<DbContextOptionsBuilder> optionsAction,
ServiceLifetime contextLifetime,
ServiceLifetime optionsLifetime
)

扫描所有程序集中的类型并筛选 DbContext

1
2
3
4
5
foreach (var asmToLoad in assemblies)
{
Type[] typesInAsm = asmToLoad.GetTypes();
foreach (var dbCtxType in typesInAsm
.Where(t => !t.IsAbstract && typeof(DbContext).IsAssignableFrom(t)))

找出当前程序集中所有继承自 DbContext 且不是抽象类的类型。

调用泛型方法 AddDbContext<TContext>

1
2
3
4
5
6
7
8
var methodGenericAddDbContext = methodAddDbContext.MakeGenericMethod(dbCtxType);
methodGenericAddDbContext.Invoke(null, new object[]
{
services,
builder,
ServiceLifetime.Scoped,
ServiceLifetime.Singleton
});

先构造泛型方法:比如 AddDbContext<MyDbContext>()
然后反射调用它,传入四个参数。

services 依赖注入容器
builder 用户传入的 ctx => ctx.UseSqlServer(…) 委托
Scoped DbContext 实例的生命周期(推荐使用 Scoped)
Singleton 配置选项的生命周期(通常 Singleton 就可以)