Reputation: 2523
"Autofac automatically chooses the constructor with the most parameters that are able to be obtained from the container." I want it to do otherwise and choose the default constructor instead. http://code.google.com/p/autofac/wiki/Autowiring
internal class ParameterlessConstructorSelector : IConstructorSelector
{
#region Implementation of IConstructorSelector
/// <summary>
/// Selects the best constructor from the available constructors.
/// </summary>
/// <param name="constructorBindings">Available constructors.</param>
/// <returns>
/// The best constructor.
/// </returns>
public ConstructorParameterBinding SelectConstructorBinding(ConstructorParameterBinding[] constructorBindings)
{
return constructorBindings.First();
}
#endregion
}
When I wire the class, I did this:
builder.RegisterType<EmployeeFactory>()
.As<IEmployeeFactory>().UsingConstructor(new ParameterlessConstructorSelector())
.SingleInstance();
The first binding in the constructorBindings list is always the one with paremeterless constructor. Not sure if it defined first or the way autofac scans the constructors but is this the right approach to wire for parameterless constructor?
Thanks
Upvotes: 10
Views: 6607
Reputation: 9680
With recent versions of Autofac, this is very simple :
builder.RegisterType<EmployeeFactory>()
.As<IEmployeeFactory>().UsingConstructor()
.SingleInstance();
Calling "UsingConstructor" with no parameters means "use parameterless constructor". See https://autofac.org/apidoc/html/EB67DEC4.htm and related pages.
Upvotes: 3
Reputation: 8295
Wouldn't it be simpler to just explicitly register the default constructor?
builder.Register<EmployeeFactory>(c => new EmployeeFactory())
.As<IEmployeeFactory>()
.SingleInstance();
Upvotes: 3
Reputation: 139758
Autofac internally uses the Type.GetConstructors
method to discover the constructors.
From the methods documentation:
The GetConstructors method does not return constructors in a particular order, such as declaration order. Your code must not depend on the order in which constructors are returned, because that order varies.
So it was just luck that it worked with the First()
in your case. In a proper implementation you need to explicitly search for the constructor with 0 arguments:
public class DefaultConstructorSelector : IConstructorSelector
{
public ConstructorParameterBinding SelectConstructorBinding(
ConstructorParameterBinding[] constructorBindings)
{
var defaultConstructor = constructorBindings
.SingleOrDefault(c => c.TargetConstructor.GetParameters().Length == 0);
if (defaultConstructor == null)
//handle the case when there is no default constructor
throw new InvalidOperationException();
return defaultConstructor;
}
}
You can test the theory with this very simple class:
public class MyClass
{
public readonly int i;
public MyClass(int i)
{
this.i = i;
}
public MyClass()
{
i = 1;
}
}
With your implementation:
var builder = new ContainerBuilder();
// register 22 for each integer constructor argument
builder.Register<int>(v => 22);
builder.RegisterType<MyClass>().AsSelf()
.UsingConstructor(new ParameterlessConstructorSelector());
var c = builder.Build();
var myClass = c.Resolve<MyClass>();
Console.WriteLine(myClass.i);
It outputs 22
e.g the constructor with the int
argument is called:
With my implementation:
//...
builder.RegisterType<MyClass>().AsSelf()
.UsingConstructor(new DefaultConstructorSelector());
//...
var myClass = c.Resolve<MyClass>();
Console.WriteLine(myClass.i);
It outputs 1
e.g the default constructor is called.
Upvotes: 7