[英]OWIN Middleware + Web API + 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
OWIN的Use<T>
方法將提供的T
注冊為OWIN管道中的單例,無論您在容器中配置該類型的生存時間如何。 因此,當您在活動范圍內解析中間件時,您(暗中)告訴OWIN永遠緩存該實例。
您有兩種選擇:
確保中間件組件可以用作單例很容易。 只需在Simple Injector中將其注冊為單例,然后在調用Verify()
,Simple Injector將檢測該組件是否可以用作單例,或者它是否具有較短的依賴關系。 但是,這確實意味着所有依賴項都應該是單例,並且運行時數據(如DbContext的數據對象和其他數據對象)應在對象圖建立之后通過方法調用傳遞。 我認為這是一種很好的做法,但這可能對您的應用程序造成了很大的變化,並且可能帶來了很大的轉變。 因此,我認為這超出了此問題的范圍,因此您應該選擇選項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
參數已從構造函數中刪除,並在Invoke
方法中被Func<Task>
參數代替。 這允許Simple Injector構造類型,因為其構造函數不再包含任何運行時參數。 請記住:通過構造函數編譯時間依賴關系,通過方法調用編譯運行時數據。
另請注意,中間件不再繼承自OwinMiddleware
。 由於中間件沒有注入包裝的中間件,因此從中間件繼承變得毫無用處。
閱讀完史蒂文的答案后,這就是我所做的:
這樣注冊中間件:
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);
}
注意:我假設Simple Injector(或任何DI庫)使用CallContext
來維護作用域實例; 如果是這樣,只是想分享一下在一些中間件之后,CallContext無法正常運行。 這是我不久前發布的報告相同問題的另一個問題-OWIN中間件和CallContext
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.