C# 使用ASP.NET核心中扩展方法的中间件配置

C# 使用ASP.NET核心中扩展方法的中间件配置,c#,asp.net-core,asp.net-core-2.0,asp.net-core-middleware,C#,Asp.net Core,Asp.net Core 2.0,Asp.net Core Middleware,我开发了一个ASP.NET核心中间件。我在这篇文章中简化了中间件,以使问题更清楚 中间件在Startup.cs中配置如下: public void配置(IApplicationBuilder应用程序,IHostingEnvironment环境) { ... app.UseMyMiddleware(42); ... } 整数(42)存储在中间件实例中,一切正常 现在我想创建一些助手方法作为扩展方法: 公共静态整数添加(此整数i) { 返回i+TODO; } 我希望该方法将I的值添加到发送到中间

我开发了一个ASP.NET核心中间件。我在这篇文章中简化了中间件,以使问题更清楚

中间件在
Startup.cs
中配置如下:

public void配置(IApplicationBuilder应用程序,IHostingEnvironment环境)
{
...
app.UseMyMiddleware(42);
...
}
整数(
42
)存储在中间件实例中,一切正常

现在我想创建一些助手方法作为扩展方法:

公共静态整数添加(此整数i)
{
返回i+TODO;
}
我希望该方法将
I
的值添加到发送到中间件的值(
42
)中

目前,我已将
42
的值保存在中间件内部的公共静态变量中,并通过扩展方法访问该值:

公共静态整数添加(此整数i)
{
返回i+MyMiddleware.Value;
}

出于各种原因,我不喜欢这个解决方案。任何关于如何更好地解决此问题的想法都将不胜感激。

为什么不使用appsettings.json存储此信息,然后使用内置DI将其注入中间件

appsettings.json

{
    "MiddlewareConfiguration":
    {
        "MiddlewareValue":42,
    }
}
Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.Configure<MiddlewareConfiguration>(configuration.GetSection("MiddlewareConfiguration"));
}
public class MiddlewareConfiguration
{
    public int MiddlewareValue { get; set; }
}
然后,您可以将其注入中间件,例如:

public async Task Invoke(HttpContext context, IOptions<MiddlewareConfiguration> config)
public异步任务调用(HttpContext上下文,IOptions配置)

一般来说,这不适用于扩展方法。扩展方法是静态的,因此只能访问其他静态成员。然而,ASP.NET核心在设计上不使用任何静态,广泛用于管理对象的生命周期和访问依赖项

因此,通常,正确的方法是将扩展方法移动到某种非静态辅助对象中,然后可以将其注入到需要访问它的地方。当然,这不允许您在任何需要它的地方轻松地调用它,但需要您显式地依赖它(这是依赖注入的要点之一)

也就是说,你的明确案例有点不同。正如我从您的中看到的,您的扩展方法如下所示(注意,我在这里结合了两种方法来说明):

这有什么关系?因为您正在传递
HttpContext
。HTTP上下文允许我们访问依赖注入。因此,我们实际上可以从依赖项注入容器中解决问题:

public static async Task ShipAsync(this Exception exception, HttpContext context)
{
    var elmahIoConfig = context.RequestServices.GetService<ElmahIoConfiguration>();

    await MessageShipper.ShipAsync(
        elmahIoConfig.ApiKey,
        elmahIoConfig.LogId,
        exception.Message, context, new ElmahIoSettings(), exception);
}
公共静态异步任务shipsync(此异常,HttpContext上下文)
{
var elmahIoConfig=context.RequestServices.GetService();
等待MessageShipper.ShipAsync(
elmahIoConfig.ApiKey,
elmahIoConfig.LogId,
异常。消息,上下文,新ElmahIoSettings(),异常);
}
现在,您只需要确保注册中间件时正确填写
ElmahIoConfiguration
。请注意,我在这里没有请求中间件实例,因为中间件通常不使用依赖项注入注册。引入一个单独的对象更有意义,然后可以将它注入中间件并在那里初始化


在您的情况下,您应该真正将中间件配置移动到应用程序设置中,并使用
IOptions
模式在
ConfigureServices
级别对其进行配置。在ASP.NET Core 2中,在
Configure
中将中间件添加到管道中时对其进行配置已不再是一种常见的模式。

您可以将中间件类实例添加到IoC容器中,并在需要时使用注入来获取对它的引用。使用这种扩展方法不会有多大成功。您不能用它来解析任何非静态内容,ASP.NET Core by design可以避免静态。正确的方法是创建某种帮助器,您需要通过依赖项注入来解决这些帮助器也就是说,这究竟是一种什么样的价值呢?这是一个特定于请求的值(那么静态方法无论如何都是错误的),还是只构造了一次?它是代码中固定的常量,还是来自配置?@poke每个应用程序的值实际上是静态的,只创建一次。能够在config中指定值并在不同的地方注入该值(如另一篇文章和一个答案中所建议的)肯定会改进代码。但是它不能使值从扩展方法中可用。@如果你想看一些真实的代码,下面是我制作的一个PR,它实现了静态变量(我不喜欢):将值移动到
appsettings.json
并添加到DI不会使它从扩展方法中可用。我同意,指定这样的设置是一个很好的解决方案,但我需要的是基本上能够从静态方法访问它们。抱歉,我一定误解了这个问题,为什么需要通过静态方法访问它?通常我不需要从静态方法访问它。但是在这种情况下,我希望能够手动触发中间件内部的逻辑。它是中间件的一个异常日志记录部分。在本例中,我希望能够手动记录异常,方法是在异常类catch(exception e)e.LogThis()上创建一个日志扩展方法。不知道
HttpContext
上的
RequestServices
属性。现在唯一缺少的是,我的
Use
方法需要注册
ElmahIoConfiguration
对象。你知道这是否可能吗?我似乎无法通过app builder找到对服务集合的引用。考虑到这一点,添加一个新的
AddElmahIo
-方法(需要所有配置)可能会更好。然后要求客户端从
ConfigureServices
-方法调用它。
AddElmahIo
-方法将向服务集合添加一个单例对象。
UseElmahIo
-方法
public static async Task ShipAsync(this Exception exception, HttpContext context)
{
    var elmahIoConfig = context.RequestServices.GetService<ElmahIoConfiguration>();

    await MessageShipper.ShipAsync(
        elmahIoConfig.ApiKey,
        elmahIoConfig.LogId,
        exception.Message, context, new ElmahIoSettings(), exception);
}