一瞬间醒悟 發表於 2025-7-17 10:04:00

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

<h1 id="net-minimal-apis实现动态注册服务">.Net Minimal APIs实现动态注册服务</h1>
<h2 id="前言">前言</h2>
<p>dotnet Minimal APIs实现动态注册端点</p>
<p>上一篇文章讲解了在<code>.Net Minimal APIs</code>如何动态注册端点,这篇文章来讲解一下如何动态注册服务</p>
<p>文件层级结构如下:</p>
<pre><code class="language-markdown">SharpIcoWeb
├── Endpoints
│   ├── Internal
│   │   ├── EndpointExtensions.cs
│   │   ├── IEndpoint.cs
│   ├── IcoEndpoints.cs
│   ├── testEndpoints.cs
├── Program.cs
</code></pre>
<p>需要修改<code>EndpointExtensions</code>动态注册扩展类、<code>IEndpoint</code>端点注册接口和<code>Program.cs</code>配置类来实现端点+服务的自动注册,当然端点类也需要实现<code>IEndpoint</code>接口新增的方法。</p>
<h2 id="回顾">回顾</h2>
<p>在开始之前回顾一下如何动态注册端点类:</p>
<pre><code class="language-c#">public static class EndpointExtensions
{
    public static void MapAllEndpoints(this IEndpointRouteBuilder app)
    {
      var endpointTypes = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(t =&gt; typeof(IEndpoint).IsAssignableFrom(t) &amp;&amp; !t.IsInterface &amp;&amp; !t.IsAbstract);

      foreach (var type in endpointTypes)
      {
            type.GetMethod(nameof(IEndpoint.MapEndpoints))?
            .Invoke(null, new object[] { app });
      }
    }
}
</code></pre>
<h3 id="代码详解">代码详解:</h3>
<p>主要注册方法在扩展类中,主要分为2步。</p>
<h4 id="查找">查找</h4>
<p>第一步: 查找所有实现了<code>IEndpoint</code>的类</p>
<ul>
<li><code>Assembly.GetExecutingAssembly()</code> - 获取当前正在执行的程序集</li>
<li><code>GetTypes()</code> - 获取程序集中所有的类型</li>
<li><code>Where(...)</code> - 筛选条件:
<ul>
<li><code>typeof(IEndpoint).IsAssignableFrom(t)</code> - 类型必须实现 <code>IEndpoint</code> 接口</li>
<li><code>!t.IsInterface</code> - 排除接口本身</li>
<li><code>!t.IsAbstract</code> - 排除抽象类(不能被实例化的类)</li>
</ul>
</li>
</ul>
<h4 id="调用">调用</h4>
<p>第二步:对每个端点类,调用它的静态<code>MapEndpoints</code>方法</p>
<ul>
<li><code>foreach</code> - 遍历前面找到的所有端点类型</li>
<li><code>type.GetMethod(nameof(IEndpoint.MapEndpoints))</code> - 获取名为"<code>MapEndpoints</code>"的方法
<ul>
<li><code>nameof(IEndpoint.MapEndpoints)</code> - 安全地获取方法名</li>
</ul>
</li>
<li><code>?.Invoke(null, new object[] { app })</code> - 如果方法存在,则调用它
<ul>
<li><code>null</code> - 表示是静态方法(不需要实例)</li>
<li><code>new object[] { app }</code> - 传递参数(<code>IEndpointRouteBuilder</code>)</li>
</ul>
</li>
</ul>
<h2 id="开始实现">开始实现</h2>
<h3 id="iendpoint接口">IEndpoint接口</h3>
<p>首先在<code>IEndpoint</code>接口中添加用于服务注册的接口成员。</p>
<p>注意:之前的<code>MapAllEndpoints</code>重命名为<code>UseEndpoints</code>了,这个命名更加清晰。</p>
<pre><code class="language-C#">public interface IEndpoint
{
    static abstract void UseEndpoints(IEndpointRouteBuilder app);
   
    // 新增 IConfiguration configuration 参数可选
    static abstract void AddServices(IServiceCollection services, IConfiguration configuration);
}
</code></pre>
<h3 id="端点类">端点类</h3>
<p>在每个端点类中实现<code>AddServices</code>方法。</p>
<pre><code class="language-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&lt;IFileService, FileService&gt;();
    }
}
</code></pre>
<h3 id="扩展类">扩展类</h3>
<p>扩展方法是实现动态注册的关键类。</p>
<pre><code class="language-c#">public static class EndpointExtensions
{
    public static void UseEndpoints&lt;TMarker&gt;(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&lt;TMarker&gt;(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&lt;TypeInfo&gt; GetEndpointTypes(Type typeMarker)
    {
      var endpointTypes = typeMarker.Assembly.DefinedTypes
            .Where(x =&gt; !x.IsAbstract &amp;&amp; !x.IsInterface &amp;&amp;
                        typeof(IEndpoint).IsAssignableFrom(x));
      return endpointTypes;
    }
}
</code></pre>
<p>这次在注册的时候使用了泛型方法指定从哪个程序集找端点,如<code>AddEndpoints&lt;TMarker&gt;</code>。</p>
<p>其他的注册端点的代码和之前类似,可以看代码详解。</p>
<p><code>AddEndpoints</code>用于动态注册服务,与注册端点不同的是注册方法为<code>AddServices</code>,且传递的参数为<code>services</code>, <code>configuration</code>。</p>
<pre><code class="language-c#"> endpointType.GetMethod(nameof(IEndpoint.AddServices))!
                .Invoke(null, new object[] { services, configuration });
</code></pre>
<h3 id="program">Program</h3>
<p>在<code>Program.cs</code>中添加2行代码就能完成端点和服务的注册。</p>
<pre><code class="language-c#">builder.Services.AddEndpoints&lt;Program&gt;(builder.Configuration);

app.UseEndpoints&lt;Program&gt;();
</code></pre>
<h2 id="总结">总结</h2>
<p>动态注册服务的核心也是通过反射找到注册服务的静态方法并调用它。</p>
<p>使用<code>TMarker</code>泛型类型参数可以定位程序集,控制注册服务的扫描范围。</p><br><br>
来源:https://www.cnblogs.com/ZYPLJ/p/18988989
頁: [1]
查看完整版本: .Net Minimal APIs实现动态注册服务