OWIN中间件+ Web API +简单注入器

时间:2016-09-09 16:28:37

标签: c# asp.net-web-api dependency-injection owin simple-injector

我目前正在使用WebApiRequestLifestyle具有默认的范围生活方式。我想在OWIN中间件和其中一个API控制器中注入一个服务,服务的范围应该仍然是WebAPI,即对于整个请求,应该只有一个服务实例。

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实例应该相同。我该如何注册中间件?

这就是我现在正在做的事情:

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

这种方法的问题是 - 在注册期间为中间件创建了一个ITestService实例并且永远保留(如单例),并且对于每个webapi请求,都会在控制器之间创建和共享新实例(webapi范围) )

请不要指出这些问题 - WebApi + Simple Injector + OWIN

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

2 个答案:

答案 0 :(得分:7)

OWIN的Use<T>方法将提供的T注册为OWIN管道中的单例,无论您在容器中配置该类型的生命周期。因此,当您在活动作用域中解析中间件时,您(隐式)告诉OWIN永远缓存此实例。

你有两个选择:

  1. 确保中间件组件可以在OWIN管道中用作单例,或
  2. 解决每个请求中的中间件组件。
  3. 确保中间件组件可以用作单例很容易。只需在Simple Injector中将其注册为singleton,当您调用Verify()时,Simple Injector将检测此组件是否可用作单例,或者它是否具有较短生活方式的依赖项。但这确实意味着所有依赖项应该是单例,并且在构建对象图之后,应该通过方法调用传递运行时数据(如DbContext&s;和其他数据对象)。我认为这是一个很好的做法,但这可能是你的应用程序的一个很大的变化,可能是一个很大的转变。因此,我认为这个问题超出了这个问题的范围,所以你应该选择2。

    如果您的中间件组件具有较短生活方式的依赖项,则应根据请求请求解析该中间件。这意味着你不应该使用OWIN的Use<T>(middleware)方法,因为这会使它成为单身人士。

    这是怎么做的:

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

    请注意,每个请求都会解析TestMiddleware。这使Simple Injector可以完全控制构建的对象图。但这意味着您需要对TestMiddleware课程进行小幅调整。这应该是它的样子:

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

    请注意,OwinMiddleware参数已从构造函数中删除,并由Func<Task>方法中的Invoke参数替换。这允许Simple Injector构造类型,因为它的构造函数不再包含任何运行时参数。记住:通过构造函数编译时间依赖性,通过方法调用编译运行时数据。

    另请注意,中间件不再继承OwinMiddleware。由于中间件没有注入一个包装好的中间件,继承它就变得毫无用处。

答案 1 :(得分:0)

在阅读史蒂文的回答之后,这就是我所做的:

注册这样的中间件:

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

在中间件中,我使用ServiceLocator来解决依赖,正如Steven建议的那样(我知道ServiceLocator是一个反模式,但我们已经在应用程序中的一些不可避免的地方使用它了)

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

注意:我假设Simple Injector(或任何DI库)使用CallContext来维护作用域实例;如果是这样,只是想分享一些中间件后CallContext不能正常流动。这是我之前发布的另一个问题报告相同的问题 - OWIN Middleware & CallContext