.Net Minimal APIs实现动态注册服务

.Net Minimal APIs实现动态注册服务

前言

dotnet Minimal APIs实现动态注册端点

上一篇文章讲解了在.Net Minimal APIs如何动态注册端点,这篇文章来讲解一下如何动态注册服务

文件层级结构如下:

markdown 复制代码
SharpIcoWeb
├── Endpoints
│   ├── Internal
│   │   ├── EndpointExtensions.cs
│   │   ├── IEndpoint.cs
│   ├── IcoEndpoints.cs
│   ├── testEndpoints.cs
├── Program.cs

需要修改EndpointExtensions动态注册扩展类、IEndpoint端点注册接口和Program.cs配置类来实现端点+服务的自动注册,当然端点类也需要实现IEndpoint接口新增的方法。

回顾

在开始之前回顾一下如何动态注册端点类:

c# 复制代码
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了,这个命名更加清晰。

C# 复制代码
public interface IEndpoint
{ 
    static abstract void UseEndpoints(IEndpointRouteBuilder app);
    
    // 新增 IConfiguration configuration 参数可选
    static abstract void AddServices(IServiceCollection services, IConfiguration configuration);
}

端点类

在每个端点类中实现AddServices方法。

c# 复制代码
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>();
    }
}

扩展类

扩展方法是实现动态注册的关键类。

c# 复制代码
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

c# 复制代码
 endpointType.GetMethod(nameof(IEndpoint.AddServices))!
                .Invoke(null, new object[] { services, configuration });

Program

Program.cs中添加2行代码就能完成端点和服务的注册。

c# 复制代码
builder.Services.AddEndpoints<Program>(builder.Configuration);

app.UseEndpoints<Program>();

总结

动态注册服务的核心也是通过反射找到注册服务的静态方法并调用它。

使用TMarker泛型类型参数可以定位程序集,控制注册服务的扫描范围。

相关推荐
世洋Blog8 分钟前
装饰器模式实践:告别臃肿的继承链,优雅解耦初始化状态管理
unity·设计模式·c#·装饰器模式
我是唐青枫2 小时前
C#.NET ref struct 深度解析:语义、限制与最佳实践
c#·.net
火一线3 小时前
【C#知识点详解】基类、抽象类、接口类型变量与子类实例的归纳总结
开发语言·c#
Lv11770083 小时前
Visual Studio 中的密封类和静态类
ide·笔记·c#·visual studio
武藤一雄3 小时前
[奇淫巧技] WPF篇 (长期更新)
windows·microsoft·c#·.net·wpf
c#上位机5 小时前
halcon窗口显示带有箭头的直线
计算机视觉·c#·halcon
秦苒&6 小时前
【C语言指针四】数组指针变量、二维数组传参本质、函数指针变量、函数指针数组
c语言·开发语言·c++·c#
秋雨雁南飞6 小时前
C# 字符串占位
开发语言·c#
寰天柚子6 小时前
DotNetBar全面解析:.NET WinForms开发的高效UI控件库
ui·.net
追逐时光者6 小时前
精选 8 个 .NET 开发实用的类库,效率提升利器!
后端·.net