.Net Minimal APIs实现动态注册服务
前言
上一篇文章讲解了在.Net Minimal APIs
如何动态注册端点,这篇文章来讲解一下如何动态注册服务
文件层级结构如下:
SharpIcoWeb
├── Endpoints
│ ├── Internal
│ │ ├── EndpointExtensions.cs
│ │ ├── IEndpoint.cs
│ ├── IcoEndpoints.cs
│ ├── testEndpoints.cs
├── Program.cs
需要修改EndpointExtensions
动态注册扩展类、IEndpoint
端点注册接口和Program.cs
配置类来实现端点+服务的自动注册,当然端点类也需要实现IEndpoint
接口新增的方法。
回顾
在开始之前回顾一下如何动态注册端点类:
public static class EndpointExtensions
{
public static void MapAllEndpoints(this IEndpointRouteBuilder app)
{
var endpointTypes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => typeof(IEndpoint).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
foreach (var type in endpointTypes)
{
type.GetMethod(nameof(IEndpoint.MapEndpoints))?
.Invoke(null, new object[] { app });
}
}
}
代码详解:
主要注册方法在扩展类中,主要分为2步。
查找
第一步: 查找所有实现了IEndpoint
的类
Assembly.GetExecutingAssembly()
- 获取当前正在执行的程序集GetTypes()
- 获取程序集中所有的类型Where(...)
- 筛选条件:typeof(IEndpoint).IsAssignableFrom(t)
- 类型必须实现IEndpoint
接口!t.IsInterface
- 排除接口本身!t.IsAbstract
- 排除抽象类(不能被实例化的类)
调用
第二步:对每个端点类,调用它的静态MapEndpoints
方法
foreach
- 遍历前面找到的所有端点类型type.GetMethod(nameof(IEndpoint.MapEndpoints))
- 获取名为"MapEndpoints
"的方法nameof(IEndpoint.MapEndpoints)
- 安全地获取方法名
?.Invoke(null, new object[] { app })
- 如果方法存在,则调用它null
- 表示是静态方法(不需要实例)new object[] { app }
- 传递参数(IEndpointRouteBuilder
)
开始实现
IEndpoint接口
首先在IEndpoint
接口中添加用于服务注册的接口成员。
注意:之前的MapAllEndpoints
重命名为UseEndpoints
了,这个命名更加清晰。
public interface IEndpoint
{
static abstract void UseEndpoints(IEndpointRouteBuilder app);
// 新增 IConfiguration configuration 参数可选
static abstract void AddServices(IServiceCollection services, IConfiguration configuration);
}
端点类
在每个端点类中实现AddServices
方法。
public class TestEndpoints : IEndpoint
{
public static void UseEndpoints(IEndpointRouteBuilder app)
{
// .....
}
public static void AddServices(IServiceCollection services, IConfiguration configuration)
{
}
}
public class IcoEndpoints: IEndpoint
{
public static void UseEndpoints(IEndpointRouteBuilder app)
{
// .....
}
public static void AddServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped<IFileService, FileService>();
}
}
扩展类
扩展方法是实现动态注册的关键类。
public static class EndpointExtensions
{
public static void UseEndpoints<TMarker>(this IEndpointRouteBuilder app)
{
UseEndpoints(app, typeof(TMarker));
}
public static void UseEndpoints(this IEndpointRouteBuilder app, Type typeMarker)
{
var endpointTypes = GetEndpointTypes(typeMarker);
foreach (var type in endpointTypes)
{
type.GetMethod(nameof(IEndpoint.UseEndpoints))?
.Invoke(null, new object[] { app });
}
}
public static void AddEndpoints<TMarker>(this IServiceCollection services, IConfiguration configuration)
{
AddEndpoints(services, typeof(TMarker), configuration);
}
public static void AddEndpoints(this IServiceCollection services, Type typeMarker, IConfiguration configuration)
{
var endpointTypes = GetEndpointTypes(typeMarker);
foreach (var endpointType in endpointTypes)
{
endpointType.GetMethod(nameof(IEndpoint.AddServices))!
.Invoke(null, new object[] { services, configuration });
}
}
private static IEnumerable<TypeInfo> GetEndpointTypes(Type typeMarker)
{
var endpointTypes = typeMarker.Assembly.DefinedTypes
.Where(x => !x.IsAbstract && !x.IsInterface &&
typeof(IEndpoint).IsAssignableFrom(x));
return endpointTypes;
}
}
这次在注册的时候使用了泛型方法指定从哪个程序集找端点,如AddEndpoints<TMarker>
。
其他的注册端点的代码和之前类似,可以看代码详解。
AddEndpoints
用于动态注册服务,与注册端点不同的是注册方法为AddServices
,且传递的参数为services
, configuration
。
endpointType.GetMethod(nameof(IEndpoint.AddServices))!
.Invoke(null, new object[] { services, configuration });
Program
在Program.cs
中添加2行代码就能完成端点和服务的注册。
builder.Services.AddEndpoints<Program>(builder.Configuration);
app.UseEndpoints<Program>();
总结
动态注册服务的核心也是通过反射找到注册服务的静态方法并调用它。
使用TMarker
泛型类型参数可以定位程序集,控制注册服务的扫描范围。