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的Use<T>
方法将提供的T
注册为OWIN管道中的单例,无论您在容器中配置该类型的生命周期如何。因此,当您在活动范围内解析中间件时,您(隐式地)告诉OWIN永远缓存此实例。
你有两个选择:
- 确保中间件组件可以在OWIN管道中作为单例使用,或者
- 解析每个请求的中间件组件
确保中间件组件可以作为单例使用是很容易的。只要在简单注入器中把它注册为单例,当你调用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