17

I'm trying to inject a dependency into the shared layout view page to avoid having to do it in every view that uses the layout.

I've followed the guidance in the wiki for injecting dependencies into views, but the property is always null.

Can Autofac inject properties into a custom view page that is a layout file?

Here's my setup. CustomViewPage

namespace MyApp
{
    using System.Web.Mvc;

    public abstract class CustomViewPage : WebViewPage
    {
        public IHelper Helper { get; set; }
    }
}

~/Views/Shared/_Layout.cshtml

@inherits MyApp.CustomViewPage
<!DOCTYPE html>
<html>
...
@if(this.Helper.HasFoo()){@Html.ActionLink("Bar")}

Global Registration...

builder.RegisterType<Helper>().AsImplementedInterfaces();
builder.RegisterModelBinderProvider();
builder.RegisterFilterProvider();
builder.RegisterModule(new AutofacWebTypesModule());
builder.RegisterSource(new ViewRegistrationSource());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

The "child" views that use the layout do NOT derive from the CustomViewPage.

Josh
  • 2,740
  • 3
  • 27
  • 41

5 Answers5

23

Most of solutions will be just a wrapper around DependencyResolver.Current.GetService call, so it might be easier to call it directly from layout:

@{
    var helper = DependencyResolver.Current.GetService<IHelper>();
}
...
@if (helper.HasFoo()) { @Html.ActionLink("Bar") }
...

Also this way helps to make page model more SRP, because can avoid mixing service routines/models and business ones.

Stan
  • 1,931
  • 16
  • 17
1

Here is a little work around that will work with most DI frameworks.

First adjust you CustomPageView a little bit:

public abstract class CustomViewPage : WebViewPage
{
    public IHelper Helper { 
        get { return ViewData[Helper.ViewDataKey] as IHelper; }    
    }

}

Now well need to get the dependancy into your ViewData, introduce an attribute to do this:

public sealed class HelperAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;
        if (viewResult != null)
            viewResult.ViewData.Add(Helper.ViewDataKey, GetHelperFromIoC());

        base.OnResultExecuting(filterContext);
    }
}

On you action method or controller:

[Helper]
public ActionResult Index()
{
    return View();
}

And in your view you should now be able to use your Helper as expected:

 @Helper.HelloWorld()

See this blog for the original post.

shenku
  • 11,969
  • 12
  • 64
  • 118
1

Just create partial page and insert it into layout page:

@Html.Partial("_MyPartialPage");

Dependencies are injected into partial pages.

Pavel
  • 524
  • 4
  • 8
0

It's not only with AutoFac basically you can't achieve DI in layouts. You may need a reference to the IOC Container in CustomViewPage to resolve the dependencies.

Unless it's REALYYY REQUIRED just avoid DI in views (just my opinion).

From my point of view I don't see much benefits. I think you are not going to write unit tests for the base view page class do you? unless there is any special reason just avoid it. Instead of having dependency with the container it's better to have dependencies with concrete implementations.

VJAI
  • 32,167
  • 23
  • 102
  • 164
  • 3
    Any kind of reference or background into why you can't DI into layouts? – Josh Jul 22 '12 at 22:37
  • @Josh as stated above, It's my opinion and many others, I never said you can't just that it's probably not the best idea... A few reasons why not to, it'll be hard to unit test, depending on which IoC library you go with you'll need access to the HttpContext to resolve the container and you'll likely end up in a situation where your view is talking directly to a service therefore braking MVC pattern. Its up to everyone how they do things and I'm not saying that you can't just that you might want to re-think the design and make sure theres no better option – Joe_DM Feb 06 '17 at 03:25
  • @Joe_DM Can you please post your edit as a new answer? – VJAI Feb 06 '17 at 06:05
  • @VJAI sorry but it looks like the edited changes have been lost, not sure why :( If theres some way to find them in history I'd be happy to, otherwise it's too much work to re-create from scratch sorry. – Joe_DM Feb 13 '17 at 04:39
  • @VJAI I see what's happened, I've edited somebody elses answer which I thought was my answer :O If theres a way to get the code back which I put in the answer I'd be happy to post as a new answer. – Joe_DM Feb 13 '17 at 04:41
  • Did you try Profile > answers? If you can't find there then it's a good question to post in http://meta.stackexchange.com/ – VJAI Feb 13 '17 at 05:59
  • I vehemently disagree. Injecting dependencies into views is a well-known software pattern called the View-Helper Pattern. An example of this is a user view helper. @if(user.IsLoggedIn()) { Logout ... Also very nice with translation. – Bluebaron Feb 13 '19 at 07:22
0

For result with no parameters you don't need to extend your WebViewPage to pass data to. I would solve it this way: 1. Declare a class HelperActionFilter derived from ActionFilter, inject your service to it via property http://docs.autofac.org/en/latest/integration/mvc.html 2. Inside HelperActionFilter.OnActionExecuting setup ViewBag.HasFoo check it in layout.

Lapenkov Vladimir
  • 3,066
  • 5
  • 26
  • 37