4

I am trying to setup IoC using StructureMap for my ASP.NET MVC 4 site. Here is my StructureMapDependencyResolver class:

    public class StructrueMapDependencyResolver : IDependencyResolver
    {        
        public object GetService(Type serviceType)
        {
            return IocContainer.GetInstance(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return IocContainer.GetAllInstances(serviceType);
        }
    }

Here is part of my IocContainer

public static class IocContainer
{
    ....

    public static object GetInstance(Type t)
    {
        return ObjectFactory.TryGetInstance(t);
    }

    public static IEnumerable<object> GetAllInstances(Type t)
    {
        return ObjectFactory.GetAllInstances(t).Cast<object>();
    }
}

Here is what my Global.aspx.cs looks like

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            IocContainer.RegisterAllTypes(Server.MapPath("~\\Bin"), AssemblyList.MyAssemblies);
            DependencyResolver.SetResolver(new StructrueMapDependencyResolver());
        }
    } 

Finally I have a simple controller that depends on a service:

  public class ManagePostController: Controller
  {
      private ISomeService _someService;

      public ManagePostController(ISomeService svc)
      {
          _someService= svc;
      }
  }

When I start my website, got the following exception:

No parameterless constructor defined for this object.

[InvalidOperationException: An error occurred when trying to create a controller of type 'Foothill.WebAdmin.Controllers.ManagePostController'. Make sure that the controller has a parameterless public constructor.] System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +247

I am not sure where I need to change?

anaximander
  • 7,083
  • 3
  • 44
  • 62
sean717
  • 11,759
  • 20
  • 66
  • 90

2 Answers2

4

Replace your IocContainer.GetInstance(Type) implementation with the following:

public static object GetInstance(Type t)
{
    return t.IsAbstract || t.IsInterface
                    ? ObjectFactory.TryGetInstance(t)
                    : ObjectFactory.GetInstance(t);
}

The ASP.NET MVC extensibility model will attempt to resolve various components (e.g. IControllerActivator) which are optional (if you return null, MVC will use the default components instead). That's why we have the ObjectFactory.TryGetInstance call - this will only resolve a component if you explicitly configure it within the container.

For resolving controllers (which are concrete types) ObjectFactory.GetInstance should be used - this creates an instance even though the controller type was never explicitly configured.

The code snippet above is what I use in my projects and I just realized it's very similar to what is present in the StructureMap MVC4 Nuget package (see line 123 in this file).

By the way, I think you could simply use the NuGet package instead of going through these steps yourself.


Update

Regarding IControllerActivator: that's just another extensibility point. If no controller activator is registered, the dependency resolver is used instead:

If there is no IControllerActivator present in the dependency resolver, we will then ask the dependency resolver to create the concrete controller type by calling GetService(controllerType). (quote from Brad Wilson's blog)

Also, an explanation of why there are both IDependencyResolver and IControllerActivator: http://forums.asp.net/post/4241343.aspx

Community
  • 1
  • 1
Cristian Lupascu
  • 39,078
  • 16
  • 100
  • 137
  • Thanks @w0lf, it works now. But I still have the following question: Since I want to use the default controller activator, so I didn't register any concrete controller activator type in SM. Therefore ObjectFactory.TryGetInstance(t) will return null for a t == controller activator. And this will make the MVC4 to fall over to the default activator. Am I right? – sean717 Aug 11 '13 at 22:10
  • 1
    The contract of the `IDependencyResolver`'s `GetService` method states that "Implementers should return Nothing when the service cannot be found.". In other words, the implementation should always call `TryGetInstance`; not conditionally. – Steven Aug 12 '13 at 12:46
  • @Steven that works if all the Controllers in the application are manually configured in the container. StructureMap, however, offers this feature that it can (attempt to) resolve concrete types even though they are not explicitly configured. – Cristian Lupascu Aug 12 '13 at 12:48
  • Other containers (Castle, Ninject, Simple Injector) have this same behavior, but I'm not sure why that's relevant. If the container can resolve that instance (whether or not it is registered directly or not), it should resolve that instance (unless of course `TryGetInstance` does not resolve unregistered concrete types). But even if the container can resolve unregistered concrete types, it is still very advisable to register all your controllers explicitly to prevent [this problem](http://stackoverflow.com/questions/15908019/simple-injector-unable-to-inject-dependencies-in-web-api-controllers) – Steven Aug 12 '13 at 13:26
  • @Steven Exactly: in StructureMap `TryGetInstance` will return `null` for a non-registered concrete type, whereas `GetInstance` will perform the resolution. Thanks for pointing out the WebAPI problem; I haven't been aware of that. – Cristian Lupascu Aug 12 '13 at 13:43
  • Ahhh, okay. That's interesting. I didn't realize the behavior of `TryGetInstance` was a bit 'weird'. But note that although calling `GetInstance` is less brittle, you're still breaking the contract. But still, you got me something to think about for the Simple Injector MVC integration package. Perhaps breaking the contract iswould be the better choice in this case. – Steven Aug 12 '13 at 13:48
0

I had the same problem using IoC container and googled for hours about it but could not get rid of it.finally I install last update of Visual Studio(VS Version 2013 and update3 in my case) and the probelm get solved.

fbarikzehy
  • 4,885
  • 2
  • 33
  • 39