10

I have this class to be instantiated in a unittest:

public class Customer
{
    internal Customer(Guid id) {
        // initialize property
    }
}

If I instantiate the test class from another (unittests) assembly with a new Customer() works because I added [assembly: InternalsVisibleTo("MyProject.Tests")]

var sut = new Customer(Guid.NewGuid()); // works

But when i setup an autofac container in the other (unittest) assembly

var builder = new ContainerBuilder();
builder.RegisterType<Customer>().AsSelf();
var container = builder.Build();

I can't resolve with autofac.

var theParam = new NamedParameter("id", Guid.NewGuid());
_sut = container.Resolve<Customer>(theParam); // throws exception

My best guess was that the internal constructor was not available. But adding [assembly: InternalsVisibleTo("Autofac")] next to the other doesn't help.

The exception thown by Autofac is

Autofac.Core.DependencyResolutionException: 
An error occurred during the activation of a particular registration. See the inner exception for details. 
Registration: Activator = Customer (ReflectionActivator), 
Services = [MyProject.Customer], 
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, 
Sharing = None, 
Ownership = OwnedByLifetimeScope 
---> No accessible constructors were found for the type 'MyProject.Customer'. 

Can Autofac not handle internal constructors?

dampee
  • 3,392
  • 1
  • 21
  • 37

2 Answers2

20

Autofac can't locate non-public constructors because it uses the DefaultConstructorFinder class which searches only for public constructors by default.

You have to create your custom implementation of the IConstructorFinder interface like this:

public class AllConstructorFinder : IConstructorFinder 
{ 
    private static readonly ConcurrentDictionary<Type, ConstructorInfo[]> Cache =
        new ConcurrentDictionary<Type, ConstructorInfo[]>();


    public ConstructorInfo[] FindConstructors(Type targetType)
    {
        var result = Cache.GetOrAdd(targetType,
            t => t.GetTypeInfo().DeclaredConstructors.Where(c => !c.IsStatic).ToArray());

        return result.Length > 0 ? result : throw new NoConstructorsFoundException(targetType);
    } 
} 

Then you have to use the FindConstructorsWith extension method on type registration:

builder.RegisterType<Customer>()
   .FindConstructorsWith(new AllConstructorFinder())
   .AsSelf();

The InternalsVisibleToAttribute can't help in this case because it affects only the compile time.

Serafim Prozorov
  • 404
  • 3
  • 10
  • That is a very clear answer in very decent English. No need to apologize! :-) – dampee Oct 15 '18 at 14:33
  • I have to admit it sucks that Autofac needs something like this for handling internal classes. It shows that using `internal` keyword is not very popular approach. – Павле Oct 17 '19 at 10:14
  • 2
    @serafim-prozorov This is a good solution, I think, it is only necessary to add an additional condition for `AllConstructorFinder`: `...DeclaredConstructors.Where (c => !c.IsStatic)...` otherwise it is assumed that all static constructors can lead to exceptions like: “Cannot choose between multiple constructors with equal length 0 on type". – hcp Dec 02 '19 at 16:23
-1

if you want to register assembly types with at least only has one public constructor then you can use Autofac Where extension method like this.

 builder.RegisterAssemblyTypes(assembly)
        .Where(t => t.GetConstructors().Length > 0) //only public constructors
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();

Note: PublicOnly extension method registers only public types.

htekir
  • 29
  • 2
  • Thanks for trying to answer, but the question is to resolve with an internal constructor. Which invalidates your answer completely, I think. – dampee Jun 19 '22 at 08:47