簡體   English   中英

使用Simple Injector注入IUrlHelper

[英]Injecting IUrlHelper with Simple Injector

我正在使用Simple Injector開發一個ASP.NET Core應用程序,用於依賴注入任務。 我正在尋找一種方法將IUrlHelper注入控制器。 我知道IUrlHelperFactory ,但更喜歡直接注入IUrlHelper來保持模擬更簡單和更簡單。

以下問題為通過標准ASP.Net依賴注入注入IUrlHelper提供了有用的答案:

根據這些答案,我想出了一個類似的SimpleInjector注冊:

container.Register<IUrlHelper>(
    () => container.GetInstance<IUrlHelperFactory>().GetUrlHelper(
        container.GetInstance<IActionContextAccessor>().ActionContext));

它確實有效,但由於IActionContextAccessor.ActionContext在沒有活動的HTTP請求時返回null ,因此在app啟動期間調用此綁定會導致container.Verify()失敗。

(值得注意的是,來自鏈接問題的ASP.Net DI注冊也可以通過交叉布線工作,但遇到同樣的問題。)

作為一種解決方法,我設計了一個代理類......

class UrlHelperProxy : IUrlHelper
{
    // Lazy-load because an IUrlHelper can only be created within an HTTP request scope,
    // and will fail during container validation.
    private readonly Lazy<IUrlHelper> realUrlHelper;

    public UrlHelperProxy(IActionContextAccessor accessor, IUrlHelperFactory factory)
    {
        realUrlHelper = new Lazy<IUrlHelper>(
            () => factory.GetUrlHelper(accessor.ActionContext));
    }

    public ActionContext ActionContext => UrlHelper.ActionContext;
    public string Action(UrlActionContext context) => UrlHelper.Action(context);
    public string Content(string contentPath) => UrlHelper.Content(contentPath);
    public bool IsLocalUrl(string url) => UrlHelper.IsLocalUrl(url);
    public string Link(string name, object values) => UrlHelper.Link(name, values);
    public string RouteUrl(UrlRouteContext context) => UrlHelper.RouteUrl(context);
    private IUrlHelper UrlHelper => realUrlHelper.Value;
}

然后有標准注冊...

container.Register<IUrlHelper, UrlHelperProxy>(Lifestyle.Scoped);

這有效,但讓我有以下問題:

  • 有更好/更簡單的方法嗎?
  • 這只是一個壞主意嗎?

第二點:MVC架構師顯然希望我們注入IUrlHelperFactory ,而不是IUrlHelper 這是因為在創建URL Helper時需要HTTP請求(請參閱此處此處 )。 我提出的注冊確實掩蓋了這種依賴關系,但是沒有從根本上改變它 - 如果無法創建一個幫助器,我們可能只會拋出異常。 我錯過了一些比我意識到的更危險的東西嗎?

根據這些答案,我想出了一個類似的SimpleInjector注冊:

 container.Register<IUrlHelper>( () => container.GetInstance<IUrlHelperFactory>().GetUrlHelper( container.GetInstance<IActionContextAccessor>().ActionContext)); 

它確實有效,但由於IActionContextAccessor.ActionContext在沒有活動的HTTP請求時返回null,因此在app啟動期間調用此綁定會導致container.Verify()失敗。

這里的根本問題是IUrlHelper的構造需要運行時數據,並且在構造對象圖時不應使用運行時數據。 這與將運行時數據注入組件的代碼氣味非常相似。

作為一種解決方法,我設計了一個代理類......

我不考慮代理是一個解決辦法都沒有 就像我看到的那樣,你幾乎把它釘了起來。 代理(或適配器)是推遲創建運行時數據的方法。 我通常會使用適配器並定義特定於應用程序的抽象,但在這種情況下這會適得其反,因為ASP.NET Core在IUrlHelper上定義了許多擴展方法。 定義自己的抽象可能意味着必須創建許多新方法,因為您可能需要其中的幾個。

有更好/更簡單的方法嗎?

我不認為有,雖然您的代理實現可以在不使用Lazy<T>

class UrlHelperProxy : IUrlHelper
{
    private readonly IActionContextAccessor accessor;
    private readonly IUrlHelperFactory factory;

    public UrlHelperProxy(IActionContextAccessor accessor, IUrlHelperFactory factory)
    {
        this.accessor = accessor;
        this.factory = factory;
    }

    public ActionContext ActionContext => UrlHelper.ActionContext;
    public string Action(UrlActionContext context) => UrlHelper.Action(context);
    public string Content(string contentPath) => UrlHelper.Content(contentPath);
    public bool IsLocalUrl(string url) => UrlHelper.IsLocalUrl(url);
    public string Link(string name, object values) => UrlHelper.Link(name, values);
    public string RouteUrl(UrlRouteContext context) => UrlHelper.RouteUrl(context);
    private IUrlHelper UrlHelper => factory.GetUrlHelper(accessor.ActionContext);
}

IActionContextAccessorIUrlHelperFactory都是單例(或者至少,它們的實現可以注冊為singleton),因此您也可以將UrlHelperProxy注冊為singleton:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IUrlHelper, UrlHelperProxy>();

ActionContextAccessor使用AsyncLocal存儲在請求期間存儲ActionContext IUrlHelperFactory使用ActionContextHttpContext在該請求期間緩存創建的IUrlHelper 因此,對同一請求多次調用factory.GetUrlHelper會導致在該請求期間返回相同的IUrlHelper 這就是您不需要在代理中緩存IUrlHelper的原因。

另請注意,我現在在IActionContextAccessor注冊了IActionContextAccessorIUrlHelper ,而不是Simple Injector。 這表明當使用內置容器或任何其他DI容器時,此解決方案也可以正常工作 - 而不僅僅是使用Simple Injector。

這只是一個壞主意嗎?

絕對不。 我甚至想知道為什么這樣的代理實現沒有被ASP.NET核心團隊開箱即用。

MVC架構師顯然希望我們注入IUrlHelperFactory,而不是IUrlHelper。

這是因為那些架構師意識到不應該使用運行時數據構建對象圖。 這實際上也是我過去與他們討論過的事情。

我在這里看到的唯一風險是你可以在沒有web請求的上下文中注入IUrlHelper ,這將導致使用代理拋出異常。 但是,當你注入一個IActionContextAccessor時,這個問題也存在,所以我覺得這沒什么大不了的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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