Structure Map parameterless constructor error
Asked Answered
E

2

7

I am trying to set up structure map ver 3.0.5.0 with Web API 2.

I have followed this implementation: Configuring dependency injection with ASP.NET Web API 2.1

However, I am getting this error when doing a get against my ComplexesController:

An error occurred when trying to create a controller of type 'ComplexesController'. Make sure that the controller has a parameterless public constructor.

Can anyone see what is wrong with my structuremap config? The Create method never gets called.

This is my implementation:

public class StructureMapControllerActivator : IHttpControllerActivator
{
    private readonly IContainer _container;

    public StructureMapControllerActivator(IContainer container)
    {
        if (container == null) throw new ArgumentNullException("container");
        _container = container;
    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        try
        {                
            var scopedContainer = _container.GetNestedContainer();
            scopedContainer.Inject(typeof(HttpRequestMessage), request);
            request.RegisterForDispose(scopedContainer);
            return (IHttpController)scopedContainer.GetInstance(controllerType);
        }
        catch (Exception e)
        {
            // TODO : Logging
            throw e;
        }
    }
}

This method is in my startup...

public void InitializeContainer()
    {
        // STRUCTURE MAP
        Container container = new Container();
        GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));

        container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
        container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());        
    }

.. and this is the controller:

 public class ComplexesController : ApiController
{

    private IComplexRepository _repo;

    public ComplexesController(IComplexRepository repo)
    {
        _repo = repo;
    }

    // GET: api/Complexes
    public IList<Complex> GetComplexes()
    {
        var complexes = _repo.GetList();
        return complexes;
    }
    ...

My full Startup class

[assembly: OwinStartup(typeof(AngularJSAuthentication.API.Startup))]
namespace AngularJSAuthentication.API
{
  public class Startup
   {
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();
        WebApiConfig.Register(config);
        app.UseWebApi(config);
    }

  }
}
Entomophagous answered 29/7, 2014 at 18:36 Comment(0)
P
10

The problem here is that you are registering your service activator with a GlobalConfiguration object and not your HttpConfiguration object. In this scenario The GlobalConfiguration object is never used as it is replaced by the HttpConfiguration object. In order to solve your issue you should replace your InitializeContainer() method with the following.

public void InitializeContainer(HttpConfiguration config)
    {
        // STRUCTURE MAP
        Container container = new Container();
        config.Services.Replace(typeof(IHttpControllerActivator), new StructureMapControllerActivator(container));

        container.Configure(x => x.For<IForumRepository>().Use<ForumRepository>());
        container.Configure(x => x.For<IComplexRepository>().Use<ComplexRepository>());        
    }

you should then pass the HttpConfiguration object from your Startup class to the new InitializeContainer() method.

Hope this helps.

-B

Placia answered 31/7, 2014 at 9:3 Comment(2)
Thanks Brendan - well spottedEntomophagous
Just tested - Create is being called and data is coming back from the controller - sweetEntomophagous
P
0

I am trying to gain a solid understanding of the complete lifecycle. I think my setup may be slightly different to the above. Here is what worked for me.

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {                       
        var config = new HttpConfiguration();
        var container = IocConfig.Setup();

        // Allow a controller to be declared without a parameterless constructor
        config.DependencyResolver = new DependencyResolver(container);

        config.Services.Add( typeof(IExceptionLogger), new GlobalExceptionLogger( container.GetInstance<ILoggingService>()));

        // Web API routes
        config.MapHttpAttributeRoutes();

        // Setup Authentication
        ConfigureOAuth(app, container);

        var corsOptions = CorsOptions.AllowAll;

        app.UseCors(corsOptions);

        // Add ASP.Net Web API to OWIN pipeline
        app.UseWebApi(config);
    }
}

It worked after I added this line:

// Allow a controller to be declared without a parameterless constructor
config.DependencyResolver = new DependencyResolver(container);

You have to get that my var container loads from a static class called IocConfig with a static Setup method. This is where the interfaces are mapped to their concrete implementations.

Also, you can probably ignore the GlobalExceptionLogger line if you want to use my complete example.

Prussiate answered 21/3, 2016 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.