Autofac and ASP .Net MVC 4 Web API

Moon picture Moon · Mar 11, 2013 · Viewed 11.7k times · Source

I am using Autofac for IoC in my ASP .Net MVC 4 project. Autofac is having some trouble initializing the repository and passing it to the API Controller.

I am sure I am missing something in my configuration.

Here is the error I get when I navigate to: https://localhost:44305/api/integration

<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>
        None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' 
        on type 'EL.Web.Controllers.API.IntegrationController' can be invoked with 
        the available services and parameters: Cannot resolve parameter 
        'EL.Web.Infrastructure.IRepository`1[EL.Web.Models.Integration] repository' of 
        constructor 'Void .ctor(EL.Web.Infrastructure.IRepository`1[EL.Web.Models.Integration])'.
    </ExceptionMessage>
    <ExceptionType>Autofac.Core.DependencyResolutionException</ExceptionType>
    <StackTrace>
        at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) 
        at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters) 
        at Autofac.Core.Resolving.InstanceLookup.Execute() 
        at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) 
        at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) 
        at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) 
        at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) 
        at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) 
        at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters) 
        at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters) 
        at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType) 
        at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType) 
        at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) 
        at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    </StackTrace>
</Error>

Here are some relevant bits of code:

IoC Bootstrapper:

public static class Bootstrapper
{
    public static void Initialize()
    {
        var builder = new ContainerBuilder();

        builder.RegisterControllers(Assembly.GetExecutingAssembly());
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        builder.Register(x => new SharePointContext(HttpContext.Current.Request)).As<ISharePointContext>().SingleInstance();
        builder.RegisterType<SharePointRepository<IEntity>>().As<IRepository<IEntity>>();
        builder.RegisterType<SharePointContextFilter>().SingleInstance();

        builder.RegisterFilterProvider();

        IContainer container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

        var resolver = new AutofacWebApiDependencyResolver(container);
        GlobalConfiguration.Configuration.DependencyResolver = resolver;
    }
}

IRepository:

public interface IRepository<T>
{
    void Add(T entity);

    void Delete(int id);

    IEnumerable<T> Find(Expression<Func<T, bool>> filter = null);

    void Update(int id, T entity);
}

SharePointRepository:

internal class SharePointRepository<T> : IRepository<T> where T : IEntity
{
    private readonly ISharePointContext _context;
    private readonly string _listName;

    internal SharePointRepository(ISharePointContext context)
    {
        _context = context;

        object[] attributes = typeof (T).GetCustomAttributes(typeof (SharePointListAttribute), false);

        if (!attributes.Any())
        {
            throw new Exception("No associated SharePoint list defined for " + typeof (T));
        }

        _listName = ((SharePointListAttribute) attributes[0]).ListName;
    }

    public void Add(T entity)
    {
        throw new NotImplementedException();
    }

    public void Delete(int id)
    {
        throw new NotImplementedException();
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> filter)
    {
        throw new NotImplementedException();
    }

    public void Update(int id, T entity)
    {
        throw new NotImplementedException();
    }
}

IntegrationController:

public class IntegrationController : ApiController
{
    private readonly IRepository<Integration> _repository;

    public IntegrationController(IRepository<Integration> repository)
    {
        _repository = repository;
    }

    public void Delete(Guid integrationId)
    {
        _repository.Delete(Get(integrationId).Id);
    }

    public IEnumerable<Integration> Get()
    {
        return _repository.Find();
    }

    public Integration Get(Guid integrationId)
    {
        return _repository.Find(i => i.IntegrationId == integrationId).FirstOrDefault();
    }

    public void Post([FromBody] Integration integration)
    {
        _repository.Add(integration);
    }

    public void Put(Guid integrationId, [FromBody] Integration integration)
    {
        _repository.Update(Get(integrationId).Id, integration);
    }
}

IEntity:

internal interface IEntity
{
    int Id { get; }
}

Entity:

public abstract class Entity : IEntity
{
    protected Entity(int id)
    {
        Id = id;
    }

    public int Id { get; private set; }
}

Integration:

[SharePointList("Integrations")]
public class Integration : Entity
{
    public Integration(int id) : base(id)
    {
    }

    public string ApiUrl { get; set; }

    public bool DeletionAllowed { get; set; }

    public Guid IntegrationId { get; set; }

    public string Key { get; set; }

    public string List { get; set; }

    public bool OutgoingAllowed { get; set; }

    public string RemoteWeb { get; set; }

    public string Web { get; set; }
}

Answer

nemesv picture nemesv · Mar 11, 2013

You have registered your IRepository wrong. With the line:

builder.RegisterType<SharePointRepository<IEntity>>().As<IRepository<IEntity>>();

You told Autofac that whenever somebody will request an IRepository<IEntity> give them a SharePointRepository<IEntity>, but you are requesting a concrete IRepository<Integration> so you get an exception.

What you need is the open generic registration feature of Autofac. So change your registration to:

builder.RegisterGeneric(typeof(SharePointRepository<>))
       .As(typeof(IRepository<>));

It will work as you would expect you when you ask for a IRepository<Integration> it will give a SharePointRepository<Integration>.

You also have a second unrelated problem: your SharePointRepository has only an internal constructor.

Autofac by default only looks for public constructors so you either change your constructor and class to public or you need to tell to Autofac to look for NonPublic constructors with the FindConstructorsWith method:

builder
    .RegisterType<SharePointRepository<IEntity>>()
    .FindConstructorsWith(
       new DefaultConstructorFinder(type => 
          type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance))) 
    .As<IRepository<IEntity>>();