首页 > 解决方案 > 在 ASP.NET Core 中间件构造函数中混合依赖注入和手动传递的参数

问题描述

我正在为 ASP.NET Core 2.2 编写一个自定义中间件。根据Microsoft Docs on writing custom middlewares:

中间件组件可以通过构造函数参数从依赖注入 (DI) 中解析它们的依赖关系。UseMiddleware<T>也可以直接接受附加参数。

这似乎一切都很好,但它并没有说明当我混合这两种方式时会发生什么,例如使用 DI 并在UseMiddleware<T>. 例如,我有以下中间件:

public class CustomMiddleware
{
    public CustomMiddleware(RequestDelegate next, ILogger<CustomMiddleware> logger, CustomMiddlewareOptions options)
    { 
        ...
    }

    public async Task InvokeAsync(HttpContext context)
    {
        ...
    }

wherelogger由 DIoptions提供,如下所示:

app.UseMiddleware<CustomMiddleware>(new CustomMiddlewareOptions());

我自己对 2.2 的测试似乎表明这很好用,并且构造函数中参数的顺序无关紧要(我可以将 DI 参数放在手动传递的参数之前或之后,甚至可以放在两个手动传递的参数之间) . 但我正在寻找一些保证,即我正在做的事情是好的。如果有人能指出一些支持这种用法的文档或源代码,那就太好了。谢谢!

标签: c#asp.net-coredependency-injectionasp.net-core-2.2asp.net-core-middleware

解决方案


我自己对 2.2 的测试似乎表明这很好用,并且构造函数中参数的顺序无关紧要(我可以将 DI 参数放在手动传递的参数之前或之后,甚至可以放在两个手动传递的参数之间) . 但我正在寻找一些保证

是的。看完源代码,我会说没问题。

这个怎么运作

YourCustomMiddleware是一个约定俗成的中间件(不同于基于工厂的中间件),由ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs) 激活

var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length);     // 
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);

这里的args(Given Arguments) 是您传递给UseMiddleware<CustomMiddleware>(args)(不带next) 的参数数组。

准备构造函数参数时有两个阶段:

  1. 匹配给定args的构造参数类型。并在类型匹配时设置值。在此处查看源代码
  2. 使用填充null元素。请参阅ServiceProvider.GetRequiredService<SomeService>()此处的源代码。如果服务实例仍然是null,则使用该default值。

例如,假设:

  1. 您有一个约定俗成的中间件,其构造函数具有以下签名:
    public CustomMiddleware(RequestDelegate next, A a, B b, C c, D d, E e){ ... }
    
  2. 然后我们在注册中间件时传入两个参数:

    app.UseMiddleware(c, a) 
    

    其中c是 Type 的一个实例C,并且a是 Type 的一个实例A。所以givenParameters数组是[next,c, a]

要创建 的实例CustomMiddleware,编译器需要知道完整的构造函数参数值。DI 扩展_parameterValues在两个阶段内获取此构造函数参数值数组 ()。请参阅:

在此处输入图像描述

stage2 的工作方式如下:

b'= sp.GetService(B); 
if b' == null :
    b' = default value of B

正如您在上面看到的,ActivatorUtilities.CreateInstance(sp,mw,args)API 会自动处理顺序和缺失的参数。


作为旁注,按惯例中间件在启动时激活,并且始终是单例的。如果您想使用范围服务,请参阅此线程


推荐阅读