简体   繁体   中英

Configure Simple Injector to support resolving ASP.NET Core middleware dependencies?

How can I configure SimpleInjector to resolve LogMiddleware's Invoke method dependency, like IMessageService ?

As I know, Asp.net core uses HttpContext.RequestServices (IServiceProvider) to resolve dependencies, I set SimpleInjector container to HttpContext.RequestServices property but didn't work. I want to change ServiceProvider dynamically because each tenant should have a container.

public class LogMiddleware
{
    RequestDelegate next;
    private readonly ILogger log;

    public LogMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
        this.next = next;
        this.log = loggerFactory.CreateLogger<LogMiddleware>();
    }

    public async Task Invoke(HttpContext context, IMessageService messageService)
    {
         await context.Response.WriteAsync(
                messageService.Format(context.Request.Host.Value));
    }
}

public interface IMessageService
{
    string Format(string message);
}

public class DefaultMessageService : IMessageService
{
  public string Format(string message)
  {
    return "Path:" + message;
  }
}

You can use your LogMiddleware class as follows:

applicationBuilder.Use(async (context, next) => {
    var middleware = new LogMiddleware(
        request => next(),
        applicationBuilder.ApplicationServices.GetService<ILoggerFactory>());

    await middleware.Invoke(context, container.GetInstance<IMessageService>());
});

I however advise you to change your middleware class a little bit. Move the runtime data (the next() delegate) out of the constructor (since components should not require runtime data during construction ), and move the IMessageService dependency into the constructor. And replace the ILoggerFactory with a ILogger dependency, since an injection constructor should do no more than store its incoming dependencies (or replace ILogger with your own application-specific logger abstraction instead ).

Your middleware class will then look as follows:

public sealed class LogMiddleware
{
    private readonly IMessageService messageService;
    private readonly ILogger log;

    public LogMiddleware(IMessageService messageService, ILogger log) {
        this.messageService = messageService;
        this.log = log;
    }

    public async Task Invoke(HttpContext context, Func<Task> next) {
        await context.Response.WriteAsync(
            messageService.Format(context.Request.Host.Value));

        await next();
    }
}

This allows you to use your middleware as follows:

var factory = applicationBuilder.ApplicationServices.GetService<ILoggerFactory>();

applicationBuilder.Use(async (context, next) => {
    var middleware = new LogMiddleware(
        container.GetInstance<IMessageService>(),
        factory.CreateLogger<LogMiddleware>());

    await middleware.Invoke(context, next);
});

Or in case you registered the ILogger (or your custom logging abstraction) in Simple Injector, you can do the following:

applicationBuilder.Use(async (context, next) => {
    var middleware = container.GetInstance<LogMiddleware>();
    await middleware.Invoke(context, next);
});

There is two problem with your code.

  1. Your "DefaultMessageService" does not implement interface "IMessageService". It should be like this.
  public class DefaultMessageService : IMessageService { public string Format(string message) { return "Path:" + message; } } 
  1. You have to register DefaultMessageService in ConfigureService in Startup.cs.
  public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.AddSingleton<IMessageService>(new DefaultMessageService()); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM