.Net Minimal APIs實現動態注冊服務
.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泛型類型參數可以定位程序集,控制注冊服務的掃描范圍。

浙公網安備 33010602011771號