1
votes

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 i.e., 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

2
Why shouldn't we point you at these answers? Why do those answers not work for you?Steven
Btw, please show the relevant code you use to create your TestMiddleware.Steven
added the middleware registration codeGokulnath
There's not enough information for me to figure out how to recreate the issue you describe - please provide a short program to reproduce the problem.qujck

2 Answers

8
votes

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.

0
votes

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