简体   繁体   中英

OWIN Middleware + Web API + Simple Injector

I'm currently using WebApiRequestLifestyle has the default scoped lifestyle. I want to inject a service in the OWIN Middleware and in one of the API controllers, and the service's scope should be still WebAPI ie, for the entire request, there should be only one instance of the service.

public class TestMiddleware : OwinMiddleware
{
    private readonly ITestService _testService;

    public TestMiddleware(OwinMiddleware next, ITestService testService) : base(next)
    {
        _testService = testService;
    }

    public override async Task Invoke(IOwinContext context)
    {
        var test = _testService.DoSomething();
        await Next.Invoke(context);
    }
}

public class ValuesController : ApiController
{
    private readonly ITestService _testService;

    public ValuesController(ITestService testService)
    {
        _testService = testService;
    }
}

ITestService instance should be same for the entire request. How should I register the middleware?

This is how I'm doing it now:

     using (container.BeginExecutionContextScope())
        {
            var testService = container.GetInstance<ITestService>();
            app.Use<TestMiddleware>(testService);
        }

The problem with this approach is - one instance of ITestService is getting created for the middleware during registration and stays forever (like a singleton), and for every webapi request, a new instance gets created and shared across the controllers (webapi scope)

Please don't point me to these questions - WebApi + Simple Injector + OWIN

Injecting a dependency into OWIN Middleware and per web-request with Simple Injector

OWIN's Use<T> method registers the supplied T as singleton in the OWIN pipeline, no matter what lifetime you configured that type with in your container. So while you resolve the middleware in an active scope, you (implicitly) tell OWIN to cache this instance for ever.

You have two choices here:

  1. Make sure that the middleware component can be used as singleton in the OWIN pipeline, or
  2. Resolve the middleware component on each request.

Making sure the middleware component can be used as singleton is easy. Just register it as singleton in Simple Injector, and when you call Verify() , Simple Injector will detect whether or not this component can be used as singleton, or whether it has dependencies with a shorter lifestyle. This does mean however that all dependencies should be singletons and runtime data (like DbContext's and other data objects) should be passed through method calls after the object graph is built. I consider this good practice, but this might be quite a change in your application and probably quite a mind shift. Because of this I consider this to be out of scope for this question, so you should go for option 2.

In case your middleware component has dependencies with a shorter lifestyle, you are should resolve that middleware per request request. This means you should not use OWIN's Use<T>(middleware) method, because that would make it a singleton.

This is how to do it:

app.Use(async (context, next) =>
{
    var middleware = container.GetInstance<TestMiddleware>();

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

Note that the TestMiddleware is resolved on each request. This gives Simple Injector full control over the built object graph. This means however that you need to make a small adjustment to your TestMiddleware class. This is how it should look:

public sealed class TestMiddleware
{
    private readonly ITestService _testService;

    public TestMiddleware(ITestService testService)
    {
        _testService = testService;
    }

    public async Task Invoke(IOwinContext context, Func<Task> next)
    {
        var test = _testService.DoSomething();
        await next();
    }
}

Note that the OwinMiddleware parameter removed from the constructor, and replaced by a Func<Task> parameter in the Invoke method. This allows Simple Injector to construct the type, because its constructor won't contain any runtime parameters anymore. Remember: compile time dependencies through the constructor, runtime data through method calls.

Also note that the middleware doesn't inherit from OwinMiddleware anymore. Since the middleware didn't inject a wrapped middleware, inheriting from it became useless.

After reading Steven's answer, this is what I did:

Registered the middleware like this:

using (AsyncScopedLifestyle.BeginScope(ServiceLocator.Container))
{
    app.Use<TestMiddleware>();
}

Inside the middleware, I used ServiceLocator to resolve the dependency as Steven suggested (I know ServiceLocator is an anti-pattern, but we are already using it in a few unavoidable places in the app)

public override async Task Invoke(IOwinContext context)
{
    var testService = ServiceLocator.Current.GetInstance<ITestService>();
    testService.DoSomething();
    await Next.Invoke(context);
}

Note: I assume Simple Injector (or any DI library) uses CallContext to maintain the scoped instances; if so, just wanted to share that the CallContext doesn't flow properly after some middlewares. Here is my another question that I posted a while ago reporting the same - OWIN Middleware & CallContext

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