ThomasArdal
ThomasArdal

Reputation: 5239

Using middleware config from extension methods in ASP.NET Core

I've developed a piece of ASP.NET Core middleware. I've simplified the middleware in this post, to make the problem clearer.

The middleware is configured in Startup.cs like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    app.UseMyMiddleware(42);
    ...
}

The integer (42) is stored in the middleware instance and everything is fine.

Now I want to create some helper methods as extension methods:

public static int Add(this int i)
{
    return i + TODO;
}

I would like the method to add the value of i to the value (42) sent to the middleware.

Currently I've saved the value of 42 in a public static var inside the middleware and access this from my extension method:

public static int Add(this int i)
{
    return i + MyMiddleware.Value;
}

I don't like this solution for various reasons. Any ideas to how this can be solved in a better way would be highly appreciated.

Upvotes: 1

Views: 3707

Answers (2)

poke
poke

Reputation: 387775

In general, this won’t work with extension methods. Extension methods are static and as such can only access other static members. ASP.NET Core however is by design not using any statics and extensively uses dependency injection to manage the lifetime of objects and to access dependencies.

So usually, the proper way would be to move your extension method into some kind of non-static helper object which you can then inject where you need to access it. Of course, this will not allow you to easily call it wherever you need it but requires you to explicitly depend on it (which is one of the points of dependency injection).

That being said, your explicit case is a bit different. As I could see from your linked pull request, your extension method looks like this (note, I combined two methods here to make it clear):

public static async Task ShipAsync(this Exception exception, HttpContext context)
{
    await MessageShipper.ShipAsync(
        ElmahIoMiddleware.ApiKey.AssertApiKeyInMiddleware(),
        ElmahIoMiddleware.LogId.AssertLogIdInMiddleware(),
        exception.Message, context, new ElmahIoSettings(), exception);
}

Why does that matter? Because you are passing HttpContext. And the HTTP context gives us access to dependency injection. So we can actually resolve things from the dependency injection container:

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);
}

Now, you just need to make sure that ElmahIoConfiguration gets filled properly when you register the middleware. Note that I am not requesting the middleware instance here because middlewares are usually not registered with dependency injection. It makes more sense to introduce a separate object which you can then inject into the middleware and initialize there.

In your case, you should really move the middleware configuration into your application settings and use the IOptions<> pattern to configure it at ConfigureServices level. Configuring middleware as you add it to the pipeline in Configure is not really a common pattern in ASP.NET Core 2 anymore.

Upvotes: 2

Jamie Rees
Jamie Rees

Reputation: 8183

Why don't you use the appsettings.json to store this information and then use the built-in DI to inject it into your middleware?

appsettings.json

{
    "MiddlewareConfiguration":
    {
        "MiddlewareValue":42,
    }
}

Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.Configure<MiddlewareConfiguration>(configuration.GetSection("MiddlewareConfiguration"));
}

MiddlewareConfiguration.cs

public class MiddlewareConfiguration
{
    public int MiddlewareValue { get; set; }
}

You can then inject it into your middleware, e.g.:

public async Task Invoke(HttpContext context, IOptions<MiddlewareConfiguration> config)

Upvotes: 0

Related Questions