I'm working on an ASP.NET Core app using Simple Injector for dependency injection duties. I'm looking for a way to inject IUrlHelper
into controllers. I know about IUrlHelperFactory
, but would prefer to directly inject IUrlHelper
to keep things a little tidier and simpler to mock.
The following questions have useful answers for injecting IUrlHelper
through the standard ASP.Net Dependency Injection:
Based on those answers, I came up with a comparable SimpleInjector registration:
container.Register<IUrlHelper>(
() => container.GetInstance<IUrlHelperFactory>().GetUrlHelper(
container.GetInstance<IActionContextAccessor>().ActionContext));
It does work, but because IActionContextAccessor.ActionContext
returns null
when there's no active HTTP request, this binding causes container.Verify()
to fail when called during app startup.
(It's worth noting that the ASP.Net DI registrations from the linked questions also work through cross-wiring, but suffer the same problem.)
As a workaround, I've designed a proxy class...
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;
}
Which then has a standard registration...
container.Register<IUrlHelper, UrlHelperProxy>(Lifestyle.Scoped);
This works, but leaves me with the following questions:
- Is there a better/simpler way?
- Is this just a bad idea?
To the second point: The MVC architects clearly wanted us to inject IUrlHelperFactory
, and not IUrlHelper
. That's because of the need for an HTTP request when creating a URL Helper (see here and here). The registrations I've come up with do obscure that dependency, but don't fundamentally change it--if a helper can't be created, we're probably just going to throw an exception either way. Am I missing something that makes this more risky than I realize?