我目前正在使用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
答案 0 :(得分:7)
OWIN的Use<T>
方法将提供的T
注册为OWIN管道中的单例,无论您在容器中配置该类型的生命周期。因此,当您在活动作用域中解析中间件时,您(隐式)告诉OWIN永远缓存此实例。
你有两个选择:
确保中间件组件可以用作单例很容易。只需在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