Print

Simple Injector: Working with multiple constructors

Although limited in features, the Simple Injector has simple but flexible way to add features, such as the possibility to work with multiple constructors.

WARNING: The information in this article is outdated. Please see the Simple Injector documentation on how to work with multiple constructors.

I read Remo Gloor's weblog today. Remo Gloor is a developer on Ninject. In his latest blog post, he describes a new feature of the coming Ninject release concerning constructor selection. The Ninject team is going to add a new feature that allows developers to configure which constructor overload Ninject should pick to create that type.

I'm not a fan of such a feature, because this steers developers away from a clean application design. Types that act as service components, should contain a single definition of the required dependencies. Since constructor injection is the primary way to inject our dependencies, it doesn't make much sense to have multiple (public) constructors, because we end up with multiple definitions of what dependencies a class requires.

The problem with adding features that support corner case scenarios is that they tend to pollute the framework’s API. While existing users won't notice the slowly increasing API surface (just like frogs tend to stay in the water when it is heated slowly), new users get overwhelmed. Because of this I try to keep the feature set of Simple Injector minimal. Even the feature set of the SimpleInjector.Extensions library is fairly limited.

Funny thing however, is that such a feature, as Remo is adding in the next Ninject version, is already very easy to implement with the Simple Injector, simply by adding an extension method. Here is an example:

Update [2011-12-13]: This code was updated for the Simple Injector v1.3 release.

public static void Register<TService, TImplementation>(
this Container container, IConstructorSelector selector)
where TService : class
{
container.Register<TService>(() => null);

container.ExpressionBuilt += (sender, e) =>
{
if (e.RegisteredServiceType == typeof(TService))
{
var ctor =
selector.GetConstructor(typeof(TImplementation));

var parameters =
from p in ctor.GetParameters()
select container.GetRegistration(p.ParameterType, true)
.BuildExpression();

e.Expression = Expression.New(ctor, parameters);
}
};
}

This extension method does two things. First it registers the service with a 'fake' delegate. This ensures that nobody can exidentally override the implementation, and it ensures the ExpressionBuilt event will get triggered (since it only gets triggered for registered types). Next it hooks a delegate to the ExpressionBuilt event. The ExpressionBuilt event will get raised every time the container builds an Expression object for the creation of a service type, but before this Expression gets compiled to a Func<T> delegate. This allows us to influence how the container creates a type, which is exactly what we're doing here. When the delegate gets called, we first check whether the event gets raised for our TService (otherwise we skip), and if so, we fetch the constructor we wish to use using the supplied IConstructorSelector instance (shown below). Using this constructor we create a NewExpression instance that allows us to create a new instance of the given TImplementation using retrieved constructor, by supplying it the correct set of parameters.

Here is the IConstructorSelector interface with with a convenient default implementation:

public interface IConstructorSelector
{
ConstructorInfo GetConstructor(Type type);
}

public sealed class ConstructorSelector : IConstructorSelector
{
public static readonly IConstructorSelector MostParameters =
new ConstructorSelector(type => type.GetConstructors()
.OrderByDescending(c => c.GetParameters().Length).First());

public static readonly IConstructorSelector LeastParameters =
new ConstructorSelector(type => type.GetConstructors()
.OrderBy(c => c.GetParameters().Length).First());

private readonly Func<Type, ConstructorInfo> selector;

public ConstructorSelector(Func<Type, ConstructorInfo> selector)
{
this.selector = selector;
}

public ConstructorInfo GetConstructor(Type type)
{
return this.selector(type);
}
}

With this in place, we can simply register a multi constructor type as follows:

container.Register<IService, MyServiceImpl>(
ConstructorSelector.MostParameters);

Because we use Expression objects here, retrieving instances like this pure fire, just as it is with the normal Register<TService, TImplementation>() method.

I like to repeat that I discourage anyone to specify multiple constructors on a type, so the usefulness of this feature is limited. Nevertheless did it give me the possibility to showcase the extendibility of the Simple Injector :-).

Happy injecting.

- .NET General, C#, Dependency injection, Simple Injector - two comments / No trackbacks - §

The code samples on my weblog are colorized using javascript, but you disabled javascript (for my website) on your browser. If you're interested in viewing the posted code snippets in color, please enable javascript.

two comments:

Why do you discourage users from using multiple constructors? I do most often have more than one. The reason is simple: To make it easier to support unit testing.

I've created a tiny ioc which supports multiple constructors. What it does is to go through all constructors (starting with the most specific one) and try to satisfy all all dependencies. It works well and I've never had any problems with it.
jgauffin (URL) - 30 05 11 - 09:58

I create one or multiple factory methods that create the class under test. Not only does this make my tests clean, but since I'm doing that, I never had any reason to have more than one constructor in a service component.

In my experience, the cleanest application design (using DI) leads to the cleanest set of unit tests. Of course it is easy to define some sort of overload resolution inside of Simple Injector, but that is not the point. The point is that it obscures the design of your application. Note that the users of your framework must remember what the overload resolution rules are and this will always lead to surprises. What does your container do when you define a class containing two constructors with the same number of arguments? In fact, most DI frameworks for .NET have a overload resolution, but they all behave differently in this area!

Besides this, I tried to create Simple Injector in such way that it would be easy for users to migrate to another DI container. Because all containers behave different in this way, allowing such feature would make migration much harder, because it would force users to change code in the application, just to move to another DI framework.

I won't say that there will never be a good reason for a service component to have multiple public constructors, but I believe this is pretty rare and there are other ways around this. As I said, for unit tests, factory method are very useful. They make tests much cleaner, even for service that have a single public constructor. I learned this a few years back after reading Roy Osherove's The Art Of Unit Testing. If you haven’t read it already, I can recommend it.
Steven (URL) - 30 05 11 - 12:57