[英]Injecting different dependencies with Simple Injector depending on assembly
[英]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);
}
IActionContextAccessor
和IUrlHelperFactory
都是单例(或者至少,它们的实现可以注册为singleton),因此您也可以将UrlHelperProxy
注册为singleton:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IUrlHelper, UrlHelperProxy>();
ActionContextAccessor
使用AsyncLocal
存储在请求期间存储ActionContext
。 IUrlHelperFactory
使用ActionContext
的HttpContext
在该请求期间缓存创建的IUrlHelper
。 因此,对同一请求多次调用factory.GetUrlHelper
会导致在该请求期间返回相同的IUrlHelper
。 这就是您不需要在代理中缓存IUrlHelper
的原因。
另请注意,我现在在IActionContextAccessor
注册了IActionContextAccessor
和IUrlHelper
,而不是Simple Injector。 这表明当使用内置容器或任何其他DI容器时,此解决方案也可以正常工作 - 而不仅仅是使用Simple Injector。
这只是一个坏主意吗?
绝对不。 我甚至想知道为什么这样的代理实现没有被ASP.NET核心团队开箱即用。
MVC架构师显然希望我们注入IUrlHelperFactory,而不是IUrlHelper。
这是因为那些架构师意识到不应该使用运行时数据构建对象图。 这实际上也是我过去与他们讨论过的事情。
我在这里看到的唯一风险是你可以在没有web请求的上下文中注入IUrlHelper
,这将导致使用代理抛出异常。 但是,当你注入一个IActionContextAccessor
时,这个问题也存在,所以我觉得这没什么大不了的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.