繁体   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