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

本文关键字:简单 注入器 API Web 中间件 OWIN | 更新日期: 2023-09-27 18:06:19

我目前使用的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

使用Simple Injector向OWIN中间件和每个web请求注入依赖

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

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

你有两个选择:

  1. 确保中间件组件可以在OWIN管道中作为单例使用,或者
  2. 解析每个请求的中间件组件

确保中间件组件可以作为单例使用是很容易的。只要在简单注入器中把它注册为单例,当你调用Verify()时,简单注入器就会检测这个组件是否可以作为单例使用,或者它是否有更短的依赖关系。这意味着所有的依赖关系都应该是单例的,并且运行时数据(如DbContext和其他数据对象)应该在对象图构建后通过方法调用传递。我认为这是一种很好的做法,但这可能会对您的应用程序造成很大的改变,而且可能会引起很大的思想转变。因此,我认为这超出了这个问题的范围,所以你应该选择选项2。

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

方法如下:

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

请注意,TestMiddleware在每个请求上都被解析。这使得简单注入器可以完全控制构建的对象图。这意味着你需要对你的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参数从构造函数中删除,并在Invoke方法中被Func<Task>参数所取代。这允许简单注入器构造该类型,因为它的构造函数不再包含任何运行时参数。记住:通过构造函数编译时间依赖,通过方法调用编译运行时数据。

还要注意中间件不再从OwinMiddleware继承。由于中间件没有注入包装的中间件,从它继承就变得没有用了。

看了Steven的回答后,我是这样做的:

像这样注册中间件:

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

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

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

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