C# ASP.NET核心中间件构造函数中混合依赖注入和手动传递参数

C# ASP.NET核心中间件构造函数中混合依赖注入和手动传递参数,c#,asp.net-core,dependency-injection,asp.net-core-2.2,asp.net-core-middleware,C#,Asp.net Core,Dependency Injection,Asp.net Core 2.2,Asp.net Core Middleware,我正在为ASP.NET Core 2.2编写一个自定义中间件。根据关于定制中间件的书面说明: 中间件组件可以通过构造函数参数从依赖项注入(DI)中解析它们的依赖项使用中间件也可以直接接受其他参数 这似乎很好,但它并没有说明当我混合使用这两种方法时会发生什么,例如在usemidware中使用DI和pass参数。例如,我有以下中间件: public class CustomMiddleware { public CustomMiddleware(RequestDelegate next, I

我正在为ASP.NET Core 2.2编写一个自定义中间件。根据关于定制中间件的书面说明:

中间件组件可以通过构造函数参数从依赖项注入(DI)中解析它们的依赖项<代码>使用中间件也可以直接接受其他参数

这似乎很好,但它并没有说明当我混合使用这两种方法时会发生什么,例如在
usemidware
中使用DI和pass参数。例如,我有以下中间件:

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

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

但我在寻找一些保证,我所做的是好的

这是基于意见的。在你的情况下,一切都正常,我相信。但我更喜欢使用ASP.NET核心中引入的

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<CustomMiddleware> _logger;
    private readonly CustomMiddlewareOptions _options;

    public CustomMiddleware(RequestDelegate next, ILogger<CustomMiddleware> logger, IOptions<CustomMiddlewareOptions> options)
    {
        _next = next;
        _logger = logger;
        _options = options.Value;
    }
    //...
}
在这种情况下,您应该添加不带参数的中间件

app.UseMiddleware<CustomMiddleware>();
app.UseMiddleware();
注意

这也值得一看

public RequestLocalizationMiddleware(RequestDelegate下一步,IOptions选项)
以及框架如何为您提供在中使用中间件的功能

公共静态IApplicationBuilder用户请求本地化(此IApplicationBuilder应用程序)
{
//省略
返回app.useMediddleware();
}

public静态IAApplicationBuilder用户请求本地化(此IAApplicationBuilder应用程序,请求本地化选项)
{
//省略
返回app.useMidleware(Options.Create(Options));
}
正如您所看到的,
ASP.NET核心
开发人员也更喜欢在中间件构造函数中使用
IOptions
,在手动指定附加参数时,他们只需将其包装到
选项中

我自己对2.2的测试似乎表明,这很好,构造函数中参数的顺序并不重要(我可以将DI参数放在手动传递的参数之前或之后,甚至放在两个手动传递的参数之间)。但我想得到一些保证

对。在阅读了源代码之后,我会说它很好

工作原理 您的
CustomMiddleware
是一个按约定的中间件(不同于基于工厂的中间件),它由以下激活:

var-ctorArgs=新对象[args.Length+1];
ctorArgs[0]=下一个;
Array.Copy(args,0,args,1,args.Length);//
var instance=ActivatorUtilities.CreateInstance(app.ApplicationServices、中间件、参数);
这里的
args
(给定参数)是传递到
usemidleware(args)
(不带
下一个
)的参数数组

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

  • 将给定的
    args
    与构造参数类型匹配。并在类型匹配时设置值。看
  • 使用
    ServiceProvider.GetRequiredService()
    填充
    null
    元素。请参阅。如果服务实例仍然为
    null
    ,则使用
    默认值
  • 例如,假设:

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

    app.UseMiddleware(c, a) 
    
    其中,
    c
    c
    类型的实例,
    a
    a
    类型的实例。因此
    givenParameters
    数组是
    [下一步,c,a]

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

    阶段2的工作方式如下所示:

    b'= sp.GetService(B); 
    if b' == null :
        b' = default value of B
    
    如上所示,
    ActivatorUtilities.CreateInstance(sp、mw、args)
    API自动处理顺序和缺少的参数


    作为补充说明,by convention中间件在启动时激活,并且始终是单例的。如果要使用作用域服务,请参阅

    public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder app)
    {
        //omitted
        return app.UseMiddleware<RequestLocalizationMiddleware>();
    }
    
    public static IApplicationBuilder UseRequestLocalization(this IApplicationBuilder app, RequestLocalizationOptions options)
    {
        //omitted
        return app.UseMiddleware<RequestLocalizationMiddleware>(Options.Create(options));
    }
    
    public CustomMiddleware(RequestDelegate next, A a, B b, C c, D d, E e){ ... }
    
    app.UseMiddleware(c, a) 
    
    b'= sp.GetService(B); 
    if b' == null :
        b' = default value of B