"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
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.
Wouldn't it be simpler to just explicitly register the default constructor?
builder.Register<EmployeeFactory>(c => new EmployeeFactory())
.As<IEmployeeFactory>()
.SingleInstance();
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With