2

I'm using MVC4 and Unity 2.1. My services require a service key based on credentials retrieved from session state.

I register my service(s) like so:

container.RegisterType<IInventoryService, InventoryService>();

The constructor for InventoryService is equally simple:

public InventoryService(ServiceKey serviceKey) {  ...  }

In my website when I've needed a service I use a service locator that automatically composes the service key using credentials from session.

public static T Resolve<T>(ServiceKey serviceKey = null)
    {
        if (serviceKey == null)
        {
            serviceKey = SessionManager.ServiceKey;
        }

        var parameterOverride = new ParameterOverride(SERVICEKEY_PARAMETERNAME, serviceKey);

        return Resolve<T>(null, parameterOverride);
    }

This has worked well. The problem is that I'm now converting my site to MVC and attempting to inject services into controllers using a simple dependency resolver that uses my exiting service locator (dependency factory):

public class CustomDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        return MvcDependencyFactory.Resolve(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return MvcDependencyFactory.ResolveAll(serviceType);
    }
}

My controller looks like:

public InventoryController(IInventoryService inventoryService) { ... }

The problem is that MVC still complains about not finding a parameterless constructor when trying to instantiate the inventory controller. I think this is because I haven't registered a service key in Unity. But if I try doing so, I find that MVC is trying to resolve the controllers, and subsequently the services, before session has even been constructed.

Am I not thinking about this correctly? Each step feels pretty reasonable -- using session credentials in a service, using a service in a controller, using a resolver to help build the controller -- but I've been beating my head against the wall getting this to work.

DuncanMack
  • 311
  • 3
  • 17

3 Answers3

1

You can use the InjectionFactory in Unity (Microsoft.Practices.Unity.InjectionFactory) to specify a function to handle the resolution of your dependency. This function will only be executed when the dependency is resolved. In the below example, "c" is your Unity container passed as a argument so that you can do additional resolves within your function.

replace:

container.RegisterType<IInventoryService, InventoryService>();

with:

container.RegisterType<IInventoryService>(new InjectionFactory(c =>
    new InventoryService(SessionManager.ServiceKey)));
agartee
  • 445
  • 2
  • 15
  • Interesting. That helps clarify, though I'm still getting the error about not being able to find a parameterless constructor even after replacing that line of code. Perhaps that extends beyond the scope of this question, since the session data seems to be accounted for now. Perhaps it's something else in my dependency graph. – DuncanMack Aug 16 '13 at 16:35
  • You shouldn't have a parameter-less constructor. You should resolve your objects via your IoC container and let it inject the dependencies (and your dependencies' dependencies and so-forth). "container.Resolve();" would return the InventoryService with your service key. – agartee Aug 16 '13 at 18:24
  • If you want a parameter-less constructor, you could also inject property values. I personally prefer constructor injection, but the following link should help with the alternatives: http://stackoverflow.com/questions/515028/setter-property-injection-in-unity-without-attributes – agartee Aug 16 '13 at 18:27
  • Right, that's what's puzzling. I don't have a parameterless constructor on my InventoryController -- I inject the service, which in turn takes a service key. Your point about the injection factory seems to address the session issue for the service, but MVC is still complaining about not having a parameterless constructor on InventoryController (which is true...but shouldn't be an issue given the use of Unity). – DuncanMack Aug 16 '13 at 18:53
  • I need to do this for a for an upcoming project anyway, so I'll dig into this some more tonight. In the meantime, see if this lab helps: http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-dependency-injection#Exercise1 – agartee Aug 16 '13 at 19:35
  • Thanks. I came across both that tutorial and the Unity.Mvc4 package. I took their implementation from source and used it...and it worked...though I really have no idea why, unless it's a lifetime issue. But I've decided to go with a different approach (that I'll post as an answer in case it proves to be helpful...) – DuncanMack Aug 16 '13 at 19:48
  • Looking at your other approach, I must say I would avoid that. Although it might work, you're basically resorting to the Service Locator anti-pattern by pulling in your dependencies rather than having them pushed. – agartee Aug 17 '13 at 00:44
0

Using the Unity.Mvc4 package seemed to fix the problem, though it's not clear to me why. But rather than use yet another package and hide away my questions, I decided to add a parameterless constructor that manually resolves itself as necessary:

public InventoryController() : this (MvcDependencyFactory.Resolve<IInventoryService>(SessionManger.ServiceKey) { }

It still allows for unit testing of the controllers (via injection) while being transparent about where the resolution is happening when the parameterless constructor is called.

DuncanMack
  • 311
  • 3
  • 17
-1

Below is a custom IDependencyResolver, which was fairly straight forward once I started to dig into how it worked and differed from IoC container resolution. You need the try/catches to capture MVC's attempted resolution of IControllerActivator (source: http://www.devtrends.co.uk/blog/do-not-implement-icontrolleractivator-in-asp.net-mvc-3). If IControllerActivator cannot be resolved, your custom IDependencyResolver will be queried for your controller instead (which will use your IoC container of choice).

I added the below class to my basic MVC4's App_Start folder:

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
using Sample.Web.Controllers;

namespace Sample.Web.App_Start
{
    public static class UnityConfig
    {
        public static void ConfigureContainer()
        {
            IUnityContainer container = BuildUnityContainer();
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        }

        private static IUnityContainer BuildUnityContainer()
        {
            var container = new UnityContainer();

            container.RegisterType<IHomeService>(new InjectionFactory( c =>
                new HomeService("this string is a dependency.")));
            container.RegisterType<IController, HomeController>("Home");

            return container;
        }
    }

    public class UnityDependencyResolver : IDependencyResolver
    {
        private readonly IUnityContainer _container;

        public UnityDependencyResolver(IUnityContainer container)
        {
            _container = container;
        }

        public object GetService(Type serviceType)
        {
            try
            {
                return _container.Resolve(serviceType);
            }
            catch (ResolutionFailedException)
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                return _container.ResolveAll(serviceType);
            }
            catch (ResolutionFailedException)
            {
                return new List<object>();
            }
        }
    }
}

Here is my simple controller:

using System.Web.Mvc;

namespace Sample.Web.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHomeService _service;

        public HomeController(IHomeService service)
        {
            _service = service;
        }

        public ActionResult Index()
        {
            ViewBag.SomeData = _service.GetSomeData();
            return View();
        }
    }

    public interface IHomeService
    {
        string GetSomeData();
    }

    public class HomeService : IHomeService
    {
        private readonly string _data;

        public HomeService(string data)
        {
            _data = data;
        }

        public string GetSomeData()
        {
            return _data;
        }
    }
}

Here is my epically huge view:

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
<p>@ViewBag.SomeData</p>
agartee
  • 445
  • 2
  • 15