簡體   English   中英

OWIN中間件+ Web API +簡單注入器

[英]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中間件中,並使用Simple Injector根據每個Web請求

OWIN的Use<T>方法將提供的T注冊為OWIN管道中的單例,無論您在容器中配置該類型的生存時間如何。 因此,當您在活動范圍內解析中間件時,您(暗中)告訴OWIN永遠緩存該實例。

您有兩種選擇:

  1. 確保可以在OWIN管道中將中間件組件用作單例,或者
  2. 在每個請求上解析中間件組件。

確保中間件組件可以用作單例很容易。 只需在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM