为什么要执行模块初始化

每个模块可能有自己的依赖服务(比如仓储、服务类、配置等),初始化时统一注册到依赖注入(DI)容器。

之前我们创建ReflectionHelper方法进行程序集的获取。
获取之后得让他们准备好运行起来。

好处:传统写法把所有服务注册写在 Program.cs,随着模块增多代码会非常混乱。而用初始化器,每个模块只管自己,清晰明了,职责单一。

找到所有实现初始化接口的类

1
2
3
4
5
6
7
8
public interface IModuleInitializer
{
/// <summary>
/// 初始化模块,将所需服务注册到 <see cref="IServiceCollection"/> 中。
/// </summary>
/// <param name="services">服务集合,用于注册依赖项。</param>
void Initialize(IServiceCollection services);
}

该接口的核心目的是为每个独立模块提供一个注册依赖注入服务的统一入口点。

1
2
3
4
5
6
7
8
public class MyModuleA : IModuleInitializer
{
public void Initialize(IServiceCollection services)
{
// 注册服务
services.AddSingleton<IMyService, MyService>();
}
}

接口的模块初始化器

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
public static class ModuleInitializerExtensions
{
/// <summary>
/// 执行指定程序集中的所有实现 <see cref="IModuleInitializer"/> 接口的模块初始化器。
/// </summary>
/// <param name="services">服务集合。</param>
/// <param name="assemblies">要扫描的程序集集合。</param>
/// <returns>服务集合本身,便于链式调用。</returns>
/// <exception cref="ApplicationException">无法创建模块初始化器实例时抛出。</exception>
public static IServiceCollection RunModuleInitializers(this IServiceCollection services,
IEnumerable<Assembly> assemblies)
{
foreach (var asm in assemblies)
{
// 获取程序集中的所有类型
Type[] types = asm.GetTypes();
// 查找所有非抽象且实现了 IModuleInitializer 接口的类型
var moduleInitializerTypes = types.Where(t => !t.IsAbstract && typeof(IModuleInitializer).IsAssignableFrom(t));
foreach (var implType in moduleInitializerTypes)
{
// 创建模块初始化器实例
var initializer = (IModuleInitializer?)Activator.CreateInstance(implType);
if (initializer == null)
{
throw new ApplicationException($"Cannot create ${implType}");
}
// 调用初始化方法
initializer.Initialize(services);
}
}
return services;
}
}
1
public static IServiceCollection RunModuleInitializers(this IServiceCollection services, IEnumerable<Assembly> assemblies)

是一个扩展方法:挂在IServiceCollection上,可以链式调用
接收一组程序集参数assembies,用于模块扫描。

什么是链式调用:链式调用(Fluent Interface 或叫 Method Chaining)是一种编程风格,其特点是连续调用多个方法,每个方法调用之后都返回当前对象或其他对象,以便继续调用下一个方法。
示例:

1
2
3
4
builder.Services
.AddControllers()
.AddSwaggerGen()
.RunModuleInitializers(assemblies);
1
2
3
foreach (var asm in assemblies)
{
Type[] types = asm.GetTypes();

遍历每个程序集,取出所有类型。

1
var moduleInitializerTypes = types.Where(t => !t.IsAbstract && typeof(IModuleInitializer).IsAssignableFrom(t));

找出所有非抽象类,实现了IModuleInitializer接口类型(包括继承的)

1
2
3
4
5
6
7
foreach (var implType in moduleInitializerTypes)
{
var initializer = (IModuleInitializer?)Activator.CreateInstance(implType);
if (initializer == null)
{
throw new ApplicationException($"Cannot create ${implType}");
}

利用反射创建模块初始化实例。
如果创建失败(返回ull),抛出异常。

1
2
    initializer.Initialize(services);
}

调用模块初始化逻辑,让模块把自己的服务注入进来。

1
return services;

返回自身,便于链式调用。