Unity and IViewEngine (MVC)

Jul 4, 2008 at 3:05 PM
Edited Jul 4, 2008 at 3:08 PM
Hi all,
I'm trying to, using Unity, setup the IViewEngine for my asp.net mvc application but with no success.

Has anyone tried it already?

Code:

web.config:

  <unity>
    <typeAliases>
      <typeAlias alias="IViewEngine" type="System.Web.Mvc.IViewEngine, System.Web.Mvc" />
      <typeAlias alias="INewsService" type="MVCUnity.Services.INewsService, MVCUnity" />
    </typeAliases>
    <containers>
      <container>
        <types>
          <type type="IViewEngine" mapTo="MvcContrib.NHamlViewEngine.NHamlViewFactory, MvcContrib.NHamlViewEngine" />
          <type type="INewsService" mapTo="MVCUnity.Services.NYTimesNewsService, MVCUnity" />
        </types>
      </container>
    </containers>
  </unity>

global.asax:

if (_container == null)
            {
                _container = new UnityContainer();
                UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
                section.Containers.Default.Configure(_container);
               
                ControllerBuilder.Current.SetControllerFactory(typeof(MvcContrib.Unity.UnityControllerFactory));

                Type[] assemblyTypes = Assembly.GetExecutingAssembly().GetTypes();
                foreach (Type type in assemblyTypes)
                {
                    if (typeof(IController).IsAssignableFrom(type))
                    {
                        _container.RegisterType(type, type);
                    }
                }
            }

Controller:

public class HomeController : Controller
    {
        /*
        [Dependency]
        public INewsService NewsService { get; set; }
        */
        private INewsService _service;
        public HomeController(INewsService service)
        {
            _service = service;
        }

        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";
            List<News> currentNews = _service.GetNews();
            ViewData["News"] = currentNews;
            return View();
        }

        public ActionResult About()
        {
            ViewData["Title"] = "About Page";

            return View();
        }
    }

Thanks,
Bruno

Jul 4, 2008 at 6:53 PM
You should ask the MVCContrib guys - we have nothing to do with their controller factory.

However, from looking at the code snippet you're showing, there doesn't seem to be anything telling Unity that it should inject the IViewEngine. There's no configuration to this effect and no [Dependency] attribute anywhere on the controller. So that would be my first suggestion; configure the specific controller class to inject the view engine at the appropriate spot.
Jul 5, 2008 at 2:46 PM
Hi.
The (home)controller also dosen't have any [Dependency] attribute (the one there is commented) and still the Unity injects the INewsService. I've managed to set the ViewEngine using:

<type type="MVCUnity.Controllers.HomeController, MVCUnity">
            <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
              <property name="ViewEngine" propertyType="System.Web.Mvc.IViewEngine, System.Web.Mvc">
                <value value="NHamlViewFactory" type="MvcContrib.NHamlViewEngine.NHamlViewFactory, MvcContrib.NHamlViewEngine" typeConverter="MVCUnity.Controllers.NHamlViewFactoryTypeConverter, MVCUnity" />
              </property>
            </typeConfig>
          </type

But still I have to specify the HomeController. If I just set the System.Web.Mvc.Controller, it dosen't get injected.

Any thoughts?

Thanks,
Bruno
Jul 6, 2008 at 12:10 AM
It injects INewsService because it's a constructor dependency; Unity will automatically pick up the longest constructor and inject it's parameters. Properties and methods must be explicitly specified.

So, one easy way to do this would be to make the view engine a parameter on your constructor.

-Chris

Jul 7, 2008 at 11:19 AM
Hi ctavares.

Two questions maybe you can help me out:
1) How does the Unity solves witch constructor to call when using DI? This because I've tried the solution of adding a new argument to the constructor (IViewEngine viewEngine) but got a error saying that a interface needed by the NhamlViewFactory constructor couldn't be initialized. The Factory has to constructors, one empty and another it a single argument that's an interface.
For now I've copied the Factory to my assembly and changed the constructors.

2) How can I tell Unity to do inheritance "search", this because if it set the Homecontroller class in the <type> element I can inject the ViewFactory, but if a change to the base class System.Web.Controller, it just gets ignored.

Thanks.
Bruno



ctavares wrote:
It injects INewsService because it's a constructor dependency; Unity will automatically pick up the longest constructor and inject it's parameters. Properties and methods must be explicitly specified.

So, one easy way to do this would be to make the view engine a parameter on your constructor.

-Chris




Jul 7, 2008 at 6:34 PM
1)

From http://msdn.microsoft.com/en-us/library/cc440940.aspx:

How Unity Resolves Target Constructors and Parameters

When a target class contains more than one constructor, Unity will use the one that has the InjectionConstructor attribute applied. If there is more than one constructor, and none carries the InjectionConstructor attribute, Unity will use the constructor with the most parameters. If there is more than one such constructor (more than one of the "longest" with the same number of parameters), Unity will raise an exception.


So, Unity is correctly choosing the constructor with the most parameters.

However, in your case, your view engine factory requires an interface. What class implements that interface? You need to configure the container with a type mapping for that interface so it knows what concrete type to create to provide the constructor parameter.

2) I'm not 100% sure I understand your question, but I think you're asking if there's a way to configure the container with information for a base class, and then use that configuration whenever you try to resolve something derived from that base class. If that's what you're asking, the answer is there is no way to do that. If it's not what you're asking, then I need more information to understand what you want.

-Chris

Jul 8, 2008 at 10:27 AM
I'll try to define the interface on the configuration for the first question and see if it works :P

So, for the 2nd question what you are telling me is that I have to specify, for each controller I create, what are the injections (in this case the view engine), even if they all have a base class? If so, that's not very developer friendly nor maintainable ...

Bruno


ctavares wrote:
1)

From http://msdn.microsoft.com/en-us/library/cc440940.aspx:

How Unity Resolves Target Constructors and Parameters

When a target class contains more than one constructor, Unity will use the one that has the InjectionConstructor attribute applied. If there is more than one constructor, and none carries the InjectionConstructor attribute, Unity will use the constructor with the most parameters. If there is more than one such constructor (more than one of the "longest" with the same number of parameters), Unity will raise an exception.


So, Unity is correctly choosing the constructor with the most parameters.

However, in your case, your view engine factory requires an interface. What class implements that interface? You need to configure the container with a type mapping for that interface so it knows what concrete type to create to provide the constructor parameter.

2) I'm not 100% sure I understand your question, but I think you're asking if there's a way to configure the container with information for a base class, and then use that configuration whenever you try to resolve something derived from that base class. If that's what you're asking, the answer is there is no way to do that. If it's not what you're asking, then I need more information to understand what you want.

-Chris