4

I am trying to get Autofac setup on my Web API 2 project. I have only used it a couple of times before. I used Nuget an installed both Autofac and Autofac.WebApi2. Then in my Startup class I did this:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {

        // Get our http configuration
        var config = new HttpConfiguration();

        // Register the Autofac middleware FIRST. This also adds
        // Autofac-injected middleware registered with the container.
        var container = ConfigureInversionOfControl(app, config);

        // Register all areas
        AreaRegistration.RegisterAllAreas();  
        GlobalConfiguration.Configure(WebApiConfig.Register);

        // Use our web api
        app.UseWebApi(config);
    }

    /// <summary>
    /// Configures Autofac DI/IoC
    /// </summary>
    /// <param name="app"></param>
    /// <param name="config"></param>
    private IContainer ConfigureInversionOfControl(IAppBuilder app, HttpConfiguration config)
    {

        // Create our container
        var builder = new ContainerBuilder();

        // You can register controllers all at once using assembly scanning...
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // Register our module
        builder.RegisterModule(new AutofacModule());

        // Build
        var container = builder.Build();

        // Lets Web API know it should locate services using the AutofacWebApiDependencyResolver
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Return our container
        return container;
    }
}

The module I created looks like this:

public class AutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {

        // Create our Singletons
        builder.RegisterType<DatabaseContext>().As<DbContext>().InstancePerRequest();

        // Create our Services
        builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
        builder.RegisterType<AnswerService>().As<IAnswerService>().InstancePerRequest();
        builder.RegisterType<CategoryService>().As<ICategoryService>().InstancePerRequest();
        builder.RegisterType<FeedService>().As<IFeedService>().InstancePerRequest();
        builder.RegisterType<FilterService>().As<IFilterService>().InstancePerRequest();
        builder.RegisterType<QuestionGroupService>().As<IQuestionGroupService>().InstancePerRequest();
        builder.RegisterType<StateService>().As<IStateService>().InstancePerRequest();

        // Create our providers
        //builder.RegisterType<AnswerProvider>().As<IAnswerProvider>().InstancePerLifetimeScope();
        builder.RegisterType<CategoryProvider>().As<ICategoryProvider>().InstancePerRequest();
        builder.RegisterType<FeedProvider>().As<IFeedProvider>().InstancePerRequest();
        builder.RegisterType<FilterProvider>().As<IFilterProvider>().InstancePerRequest();
        builder.RegisterType<QuestionGroupProvider>().As<IQuestionGroupProvider>().InstancePerRequest();
        builder.RegisterType<StateProvider>().As<IStateProvider>().InstancePerRequest();
    }
}

And I added a provider as a parameter for my API constructor like this:

// Readonly properties
private readonly ICategoryProvider _provider;

/// <summary>
/// Default constructor
/// </summary>
public CategoriesController(ICategoryProvider provider)
{
    _provider = provider;
}

But, when I try to call the API (using postman) I get this error:

{
    "message": "An error has occurred.",
    "exceptionMessage": "An error occurred when trying to create a controller of type 'CategoriesController'. Make sure that the controller has a parameterless public constructor.",
    "exceptionType": "System.InvalidOperationException",
    "stackTrace": "   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
    "innerException": {
        "message": "An error has occurred.",
        "exceptionMessage": "Type 'Piiick.Api.Controllers.CategoriesController' does not have a default constructor",
        "exceptionType": "System.ArgumentException",
        "stackTrace": "   at System.Linq.Expressions.Expression.New(Type type)\r\n   at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
    }
}

I am now stuck, I can't see where the issue is at all. The category provider has a constructor like this:

/// <summary>
/// Default constructor
/// </summary>
public CategoryProvider(IUnitOfWork unitOfWork, ICategoryService service)
{

    // Map our properties
    _unitOfWork = unitOfWork;
    _service = service;
}

and the CategoryService has a constructor like this:

/// <summary>
/// The default constructor
/// </summary>
/// <param name="unitOfWork"></param>
public CategoryService(IUnitOfWork unitOfWork)
    : base(unitOfWork)
{
}

and finally, the UnitOfWork has a constructor like this:

/// <summary>
/// Default constructor
/// </summary>
/// <param name="context">The database context</param>
public UnitOfWork(DbContext context)
{
    _context = context;
    _repositories = new Dictionary<Type, object>();
}

From what I can tell, everything is set up correctly. Can anyone spot anything I have forgotten to do?

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • Do you use OWIN? Also, please show your code that invokes `ConfigureInversionOfControl`. – Oleksandr Kobylianskyi Jan 16 '17 at 17:25
  • There must be something else, you are not showing as, I put your code to the Visual Studio and it works fine. The exception you mentioned is usually thrown, when you try to use dependency not registered in the container. Also, exception's `InnerException` property should hold information what dependency is missing – tdragon Jan 17 '17 at 07:04
  • Please show the full exception details: message, type and stack trace of the exception and **all** its inner exceptions. – Steven Jan 17 '17 at 09:50
  • I will post that, but it was much the same message, I will update the question with requested information – r3plica Jan 17 '17 at 10:20

2 Answers2

6

I found the issue, I was using HttpConfiguration to set the dependency resolver, it should have been GlobalConfiguration.Configuration.

So when I changed my line from:

config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

to

GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

Everything started working.

r3plica
  • 13,017
  • 23
  • 128
  • 290
0

Try registering the assemblies that have been loaded into the execution context of your application domain just before building your container:

var assemblies = AppDomain.CurrentDomain.GetAssemblies(); builder.RegisterAssemblyModules(assemblies);

/// <summary>
/// Configures Autofac DI/IoC
/// </summary>
/// <param name="app"></param>
/// <param name="config"></param>
private IContainer ConfigureInversionOfControl(IAppBuilder app, HttpConfiguration config)
{

    // Create our container
    var builder = new ContainerBuilder();

    // You can register controllers all at once using assembly scanning...
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // Register our module
    builder.RegisterModule(new AutofacModule());

    // [!!!] ** Register assemblies of the current domain **
    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
    builder.RegisterAssemblyModules(assemblies);

    // Build
    var container = builder.Build();

    // Lets Web API know it should locate services using the AutofacWebApiDependencyResolver
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

    // Return our container
    return container;
}

And do not forget to have defined:

public class AutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {

      ...

      builder.RegisterControllers(typeof(WebApiApplication).Assembly);
      builder.RegisterApiControllers(typeof(WebApiApplication).Assembly);
    }
}
Felipe Cruz
  • 1,119
  • 10
  • 18