Search code examples
asp.net-mvcautofac

Open generic type cannot be resolved with autofac


Heres my setup code:

Dim builder As New ContainerBuilder
Dim assemblies = BuildManager.GetReferencedAssemblies().Cast(Of Assembly).ToArray

builder.RegisterModule(Of AutofacWebTypesModule)
builder.RegisterAssemblyTypes(assemblies).AsClosedTypesOf(GetType(IGenericRepository(Of)))

'Controllers
builder.RegisterControllers(assemblies)

'Model Binders
builder.RegisterModelBinders(assemblies)
builder.RegisterModelBinderProvider()

Dim container = builder.Build

And controller code:

Public Class TestController
    Inherits System.Web.Mvc.Controller

    Public Sub New(manager As IGenericRepository(Of TestObject))

    End Sub

    Public Function Test() As ContentResult

        Return Content("OK")

    End Function

End Class

Now when i navigate to /Test/Test I get the following error:

None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyProject.TestController' can be invoked with the available services and parameters: Cannot resolve parameter 'MyProject.IGenericRepository1[MyProject.TestObject] manager' of constructor 'Void .ctor(MyProject.IGenericRepository1[MyProject.TestObject])'.

What is the proper way to register this open generic with autofac?

I cant use something like builder.RegisterGeneric(GetType(GenericRepository(Of))).As(GetType(IGenericRepository(Of))) because the concrete class is in a different, unreferenced project


Solution

  • The AsClosedTypesOf method will register closed type (not generic) that implement the specific open type.

    Your code will work if one assembly contains a closed implementation of IGenericRepository<TestObject>

    public class TestRespository : IGenericRepository<TestObject> 
    { }
    

    In your case you want to register an open generic, without assembly scanning it is done by using

    builder.RegisterGeneric(typeof(GenericRepository<>)).As(typeof(IGenericRepository<>))
    

    but due to limitation explained in this Github issue (Support registering open generic types when scanning assemblies) it is not possible.

    One common workaround is to do your own assembly scanning :

    var repositories = assembly.GetTypes().Where(t =>
    {
        return t.GetTypeInfo()
            .ImplementedInterfaces.Any(
                i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IGenericRepository<>));
    });
    
    foreach (var repository in repositories)
    {
        builder.RegisterGeneric(repository).As(typeof(IGenericRepository<>));
    }