使用Simple Injector注入IUrlHelper
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Simple Injector注入IUrlHelper相关的知识,希望对你有一定的参考价值。
我正在使用Simple Injector进行依赖注入任务的ASP.NET核心应用程序。我正在寻找一种方法将IUrlHelper
注入控制器。我知道IUrlHelperFactory
,但更愿意直接注入IUrlHelper
以保持一些更整洁和更简单的模拟。
以下问题通过标准ASP.Net依赖注入注入IUrlHelper
有用的答案:
根据这些答案,我想出了一个类似的SimpleInjector注册:
container.Register<IUrlHelper>(
() => container.GetInstance<IUrlHelperFactory>().GetUrlHelper(
container.GetInstance<IActionContextAccessor>().ActionContext));
它确实有效,但是因为当没有活动的HTTP请求时IActionContextAccessor.ActionContext
返回null
,这个绑定会导致container.Verify()
在app启动时调用失败。
(值得注意的是,来自链接问题的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请求(请参阅here和here)。我提出的注册确实掩盖了这种依赖关系,但并没有从根本上改变它 - 如果无法创建一个帮助器,我们可能只会抛出异常。我错过了一些比我意识到的更危险的东西吗?
根据这些答案,我想出了一个类似的SimpleInjector注册:
container.Register<IUrlHelper>( () => container.GetInstance<IUrlHelperFactory>().GetUrlHelper( container.GetInstance<IActionContextAccessor>().ActionContext));
它确实有效,但由于
IActionContextAccessor.ActionContext
在没有活动的HTTP请求时返回null,因此在app启动期间调用时,此绑定会导致container.Verify()
失败。
这里的根本问题是IUrlHelper
的构造需要运行时数据,并且在构造对象图时不应使用运行时数据。这与Injecting runtime data into components的代码气味非常相似。
作为一种解决方法,我设计了一个代理类......
我不认为代理是一种解决方法。就像我看到的那样,你几乎把它钉了起来。代理(或适配器)是推迟创建运行时数据的方法。我通常会使用适配器并定义特定于应用程序的抽象,但在这种情况下这会适得其反,因为ASP.NET Core在many上定义了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);
}
IActionContextAccessor
和IUrlHelperFactory
都是单身人士(或者至少,他们的实现可以注册为单身人士),所以你也可以将UrlHelperProxy
注册为单身人士:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IUrlHelper, UrlHelperProxy>();
ActionContextAccessor
在请求期间使用AsyncLocal
存储来存储ActionContext
。并且IUrlHelperFactory
使用ActionContext
的HttpContext
在该请求期间缓存创建的IUrlHelper
。因此,对同一请求多次调用factory.GetUrlHelper
将导致在该请求期间返回相同的IUrlHelper
。这就是为什么你不需要在代理中缓存IUrlHelper
。
另请注意,我现在在MS.DI中注册了IActionContextAccessor
和IUrlHelper
,而不是Simple Injector。这表明当使用内置容器或任何其他DI容器时,此解决方案也可以正常工作 - 而不仅仅是使用Simple Injector。
这只是一个坏主意吗?
绝对不。我甚至想知道为什么这样的代理实现没有被ASP.NET核心团队开箱即用。
MVC架构师显然希望我们注入IUrlHelperFactory,而不是IUrlHelper。
这是因为那些架构师意识到不应该使用运行时数据构建对象图。这实际上也是我过去和他们一起使用的东西。
我在这里看到的唯一风险是你可以在没有web请求的上下文中注入discussed,这将导致使用代理抛出异常。然而,当你注射IUrlHelper
时,这个问题也存在,所以我觉得这没什么大不了的。
以上是关于使用Simple Injector注入IUrlHelper的主要内容,如果未能解决你的问题,请参考以下文章
Simple Injector 无法在 Web API 控制器中注入依赖项
如何将 Blazor 中的所有注册服务添加到 Simple Injector?
您正在尝试解决跨线服务,但在使用 Simple Injector 的活动(Async Scoped)范围之外执行此操作