This project is read-only.

default constructor

Apr 25, 2012 at 1:41 PM

"For the container to be able to create MyClass, it should contain exactly one public constructor, but it has 2."
One of my constructors is a default constructor.  Why not allow creation using that?

Buzz

Apr 25, 2012 at 2:43 PM
Edited Jun 2, 2012 at 11:10 PM

This restriction is chosen deliberately, for two reasons.

First of all, having multiple constructors is an anti-pattern when applying Dependency Injection. The reason is that there should be a single definition of what dependencies a type needs. In that sense having multiple constructors makes no sense. Having a default constructor will probably mean that you have a default set of dependencies that will be injected into the other constructor. This pattern is called Poor Man's Injection and is an anti-pattern as well.

The second reason not to allow this, has to do with one of the design principles behind the Simple Injector. It was designed in such way that it would be easy to migrate to another container. All containers have a different overload resolution when it comes to picking the constructor. Some containers will pick the constructor with the least number of arguments, while others pick the one with the maximum number of arguments. Allowing just a single constructor argument will prevent these problems when migrating.

I hope this makes sense.

Marked as answer by dot_NET_Junkie on 11/4/2013 at 1:59 AM
Apr 25, 2012 at 4:45 PM

I heavily use interfaces to avoid dependencies on specific implementations.
I'm trying to leverage this to develop an application with a plugin architecture.  I've had the plugin concept in mind, but never really designed for it aside from following general interface inversion practices.

Now I'm taking all the libraries that implement interfaces and putting them in a PlugIn folder.  I use reflection to load them.  Since I wasn't worrying about the details of dependency injection, many of my implementations have multiple constructors, and some don't even have default constructors.  Plus, it seems fairly common that objects are not fed what they need from the outside.  Often they create what they need from the inside.  So they're not really constructor injection ready.  And this seems acceptable to me since these libraries don't have to only be used as plugins, but can just be referenced in an application project and used if desired.
So at least from that perspective, I think having multiple constructors does make sense.  Different constructors for different uses.
For my plugin purposes, I think I may to at least require a default constructor.

Based on the above you can probably conclude I'm really using SimpleInjector as a service locator. 

I understand that the container won't be able to instantiate a class if it can't create the constructor parameter types, or if it has value types.  I'm cool with this, and I'm actually thinking the desired behavior might just be to use, and only use, the default constructor.

Maybe I should modify SimpleInjector to meet my needs?  Maybe I should be using something else altogether?
If you have further advice, don't hesitate.
Thanks! 

Apr 25, 2012 at 6:34 PM
Edited Apr 26, 2012 at 12:41 PM

Adding support for multiple constructors might not help you, since I could possibly pick an overload resolution that does not work for your current set of plugins. And if you must change the plugins for this, you would as easily change them to have a single public constructor (they can have as many internal or protected constructors as they like btw).

There are alternative paths you can walk though, that might even be better. For instance, you can extend the library to register types with multiple constructors, as seen in this article. The given article allows to register a single type with multiple constructors. Perhaps this is not applicable to you, since you want to batch register the plugins.

You can still register plugins in the container by supplying the container with a created instance (singleton), or a Func<T> delegate to do so. You can batch register all plugins, but instead of letting the container create them, you can create them using Activator.CreateInstance, or some other construct.

A third option is to place a factory between the application and the plugins. Register an IPluginFactory in the container and let consumers depend on the IPluginFactory, instead directly on plugins. How the factory creates the plugins is up to him, but possibly with Activator.CreateInstance.

Apr 26, 2012 at 11:42 AM

First of all, having multiple constructors is an anti-pattern when applying Dependency Injection.

I humbly disagree. The implementer of a class are well aware of which implications multiple constructors can have. I frequently use multiple constructors in my frameworks. Usually a default constructor which uses default implementations of the dependencies.

Let's for instance take the HttpRequestDecoder in my new high performance networking framework (available at github). It has a default constructor which creates the default http decoder named HttpDecoder. It makes no sense to force the user to have knowledge of such implementation detail per default. Forcing them to inject everything would make the framework hard to use since there are many dependencies. The other constructors are just provided as a service so that the user can customize the framework if they need to (which they in the most cases don't).

So my own guideline is simply to determine if the dependency will be anything else than the default dependency and adjust the number of constructors accordingly. I'm not saying that it's a good fit for everything and everyone, but having multiple constructors do have it's uses. I would expect the container to inject using the most specific constructor.

Apr 26, 2012 at 1:28 PM
Edited Apr 27, 2012 at 7:54 AM

For reusable frameworks different rules (such as the Framework Design Guidelines) apply than for line of business (LOB) applications. The classes of my open source libraries (such as CuttingEdge.Logging) have multiple constructors, including a default constructor, since, as a framework designer, I want that library to be as usable as possible, and I can't assume all users use a DI container.

Both of you (Gauffin as framework designer, and Buzzweetman as plugin developer) have different requirements than 'normal' LOB application developers and for DI in LOB development, I still won't advice multiple constructors (for service types created by the container). Still, Simple Injector can suite the both of you, but you'll have to some manual coding to get the registration up and running.

For Buzzweetman for instance, just a little change of the code of this article, will probably fix your problems:

 

public static void Register(this Container container,
    Type concreteType, IConstructorSelector selector)
{
    container.Register(concreteType, () => null);

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

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

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

 

With this extension method, you can register all plugins in the system like this:

 

string pluginDirectory =
    Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginTypes =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension == ".dll"
    let dll = Assembly.LoadFile(file.FullName)
    from type in dll.GetExportedTypes()
    where typeof(IPlugin).IsAssignableFrom(type)
    where !type.IsAbstract
    where !type.IsGenericTypeDefinition
    select type;

foreach (var pluginType in pluginTypes)
{
    // You'll need to include SimpleInjector.Extensions.dll and add
    // the "using SimpleInjector.Extensions;" namespace.
    container.Register(pluginType, ConstructorSelector.MostParameters));
}

More information about loading plugins can be found here.

 

Last thing you probably need is to combine all plugins in a collection, so that you can inject all of them in a consumer:

 

container.RegisterAll<IPlugin>(
    from pluginType in pluginTypes
    select (IPlugin)container.GetInstance(pluginType));

 

With this last registration the list will be injected in constructors taking an IEnumerable<IPlugin>.

Note that the code above registers all plugins as transient, meaning that every time the IEnumerable<IPlugin> is iterated, new instances of each plugin are created.

As you can see, Simple Injector is extendible enough to suite your needs.

I hope this helps.

Apr 26, 2012 at 1:38 PM

Good points! 

Apr 26, 2012 at 9:32 PM

It's nice to have such a responsive codeplex discussion.  Thanks!

I plan to try your code suggestions tomorrow!

Buzz

May 4, 2012 at 8:43 PM

I've been fairly successful in using the above extension method suggestion, though I'm not trying to allow multiple constructors.  I'm requiring a default constructor and I'm only using this default constructor, ignoring any others.
The following extension method has worked for me as a substitute for Register:

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

    container.ExpressionBuilt += (sender, e) =>
    {
        if (e.RegisteredServiceType == typeof(TService))
        {
            // Expects, and only uses, the default constructor
            var ctor = typeof(TImplementation).GetConstructor(Type.EmptyTypes);

            if (ctor == null)
            {
                throw new Exception(string.Format("Could not get default constructor for {0}", typeof(TImplementation).Name));
            }

            e.Expression = System.Linq.Expressions.Expression.New(ctor);
        }
    };
}

But then I wanted to use RegisterSingle.
I modified my extension method to use an "isSingleton" parameter like this:

public static void RegisterDefaultConstructor<TService, TImplementation>(this Container container, bool isSingleton) where TService : class
{
    if (isSingleton)
    {
        container.RegisterSingle<TService>(() => null);
    }
    else
    {
        container.Register<TService>(() => null);
    }

    container.ExpressionBuilt += (sender, e) =>
    {
        if (e.RegisteredServiceType == typeof(TService))
        {
            // Expects, and only uses, the default constructor
            var ctor = typeof(TImplementation).GetConstructor(Type.EmptyTypes);

            if (ctor == null)
            {
                throw new Exception(string.Format("Could not get default constructor for {0}", typeof(TImplementation).Name));
            }

            e.Expression = System.Linq.Expressions.Expression.New(ctor);
        }
    };
}

But FuncSingletonInstanceProducer throws an exception when I try to GetInstance on an interface I registered with RegisterSingle.  The exception comes from here because instance is null:

protected override Expression BuildExpressionCore()
{
    var instance = this.instanceCreator();

    if (instance == null)
    {
        throw new ActivationException(StringResources.DelegateForTypeReturnedNull(typeof(TService)));
    }

    return Expression.Constant(instance, this.ServiceType);
}
 
I'm not having much luck getting through this.  Any help? 

May 4, 2012 at 9:25 PM
Edited May 4, 2012 at 9:33 PM

This will do the trick:

public static void RegisterSingleWithDefaultConstructor<TService, TImplementation>(
    this Container container) 
    where TService : class
    where TImplemenation : class, TService
{
    var ctor = typeof(TImplementation).GetConstructor(Type.EmptyTypes);

    if (ctor == null)
    {
        throw new ArgumentException(string.Format(
            "Could not get default constructor for {0}", 
            typeof(TImplementation).Name), "TImplementation");
    }

    container.Register<TService>(() => null);
 
    object syncRoot = new object();
    TService singleton = null;

    container.ExpressionBuilt += (sender, e) =>
    {
        if (e.RegisteredServiceType == typeof(TService))
        {
            if (singleton == null)
            {
                lock (syncRoot)
                {
                    if (singleton == null)
                    {
                        singleton = Activator.CreateInstance<TImplementation>();
                    }
                }
            }
    
            e.Expression = Expression.Constant(singleton);
        }
    };
}

 Little explanation:

  1. An extra generic type constraint 'where TImplementation : class, TService' is added, to force a compile-time error when two non-related types are used during registration.
  2. The validation of the default constructor is moved outside the ExpressionBuilt event. This allows the call to RegisterSingleWithDefaultConstructor to fail immediately, instead when the instance is first requested by the container.
  3. The container.Register<TService>(() => null) call is just a dummy. This allows the container to check if a registration for that TService has been made already (and will fail immediately when the RegisterSingleWithDefaultConstructor method is called), and will trigger the ExpressionBuilt event when the TService is requested for the first time. It doesn't matter that we use Register instead of RegisterSingle, since the built expression will be replaced completely, overriding any lifetime.
  4. Since this method registers a singleton, the most performant way to do so is to create the instance once and register a ConstantExpression using that instance. This allows the expression to be used as part of expression trees of other services that are created and compiles down to the most efficient IL code. 
  5. Activator.CreateInstance is used to create the instance. Although this method is terribly slow, it doesn't really matter, since it is called just once, and the performance penalty is negligible.
  6. A double-checked lock is used to ensure that that singleton is not created more than once (here I must assume that creating multiple instances is a problem, but perhaps it's not in your case). Although you would normally not see an ExpressionBuilt event being called multiple times, the container does not protect against this (for performance reasons) and because of this, multiple threads could simultaneously call this same event. The registered event must protect itself against this. This would normally be a no-brainer (because there usually is no shared state), but since we have some shared state (the singleton variable) we must protect it
  7. Here you enter the realm of C# closures. The singleton and syncRoot variables are declared outside the body of the anonymous method and are therefore converted (by the C# compiler) into instance fields of an anonymous type. Since this RegisterSingleWithDefaultConstructor method is only called once per TService, there will only be one instance of syncRoot and one instance of singleton per TService.

I hope this helps (and I hope I didn't scare you).

May 4, 2012 at 9:48 PM

This helps VERY much.  You didn't quite scare me, but I definitely need to look this through more to understand it.
In the meantime, and seeing it's about quitting time for the weekend, I jumped to using your code and it works!!

One less thing to worry about this weekend.  Thank you very much!
Buzz 

Jul 18, 2012 at 5:50 PM

Version 1.5.0 of the Simple Injector has just been released, which allows changing the constructor resolution behavior of the container. Changing the behavior can be done by creating a custom IConstructorResolutionBehavior and registering it in the container.Options.ConstructorResolutionBehavior property. Here is an example implementation:

// Mimics the constructor resolution behavior
// of Autofac and Unity.
// Register this as follows:
// container.Options.ConstructorResolutionBehavior =
//     new MostParametersConstructorResolutionBehavior();
public class MostParametersConstructorResolutionBehavior
    : IConstructorResolutionBehavior
{
    [DebuggerStepThrough]
    public ConstructorInfo GetConstructor(
        Type serviceType, Type impl)
    {
        ConstructorInfo[] constructors =
            GetConstructorsWithMostParameters(impl);

        if (constructors.Length == 1)
        {
            return constructors[0];
        }

        string exceptionMessage =
            BuildExceptionMessage(impl, constructors);

        throw new ActivationException(exceptionMessage);
    }

    private static ConstructorInfo[] 
        GetConstructorsWithMostParameters(Type type)
    {
        if (type.GetConstructors().Length == 0)
        {
            return new ConstructorInfo[0];
        }

        var maximumNumberOfParameters = (
            from constructor in type.GetConstructors()
            select constructor.GetParameters().Length)
            .Max();

        return (
            from constructor in type.GetConstructors()
            where constructor.GetParameters()
                .Length == maximumNumberOfParameters
            select constructor)
            .ToArray();
    }

    [DebuggerStepThrough]
    private static string BuildExceptionMessage(
        Type type, ConstructorInfo[] constructors)
    {
        if (constructors.Length == 0)
        {
            return string.Format(
                "For the container to be able to " +
                "create {0}, it should contain at " +
                "least one public constructor.", type);
        }

        return string.Format(
            "{0} contains multiple public construc" + 
            "tors that contain {1} parameters. " +
            "There can only be one public construc" + 
            "tor with the highest number of parameters.",
            type, constructors[0].GetParameters().Length);
    }
}

Aug 6, 2012 at 12:52 PM
Edited Jan 4, 2013 at 2:09 PM

Here is a custom IConstructorResolutionBehavior implementation that mimics the constructor resolution behavior of Ninject, Castle Windsor, and StructureMap:

using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using SimpleInjector.Advanced;

// Mimics the constructor resolution behavior of Ninject, 
// Castle Windsor and StructureMap.
// Register this as follows:
// container.Options.ConstructorResolutionBehavior =
//   new MostResolvableParametersConstructorResolutionBehavior(
//     container);
public class MostResolvableParametersConstructorResolutionBehavior
    : IConstructorResolutionBehavior
{
    private readonly Container container;

    public MostResolvableParametersConstructorResolutionBehavior(
        Container container)
    {
        this.container = container;
    }

    private bool IsCalledDuringRegistrationPhase
    {
    [DebuggerStepThrough] get { return !this.container.IsLocked(); } } [DebuggerStepThrough] public ConstructorInfo GetConstructor(Type serviceType, Type implementationType) { var constructor = this.GetConstructorOrNull(implementationType); if (constructor != null) { return constructor; } throw new ActivationException(this.BuildExceptionMessage( implementationType)); }

    [DebuggerStepThrough] private ConstructorInfo GetConstructorOrNull(Type type) { // We prevent calling GetRegistration during the registration // phase, because at this point not all dependencies might be // registered, and calling GetRegistration would lock the // container, making it impossible to do other registrations. return ( from ctor in type.GetConstructors() let parameters = ctor.GetParameters() orderby parameters.Length descending where this.IsCalledDuringRegistrationPhase || parameters.All(this.CanBeResolved) select ctor) .FirstOrDefault(); } [DebuggerStepThrough] private bool CanBeResolved(ParameterInfo parameter) { return this.container .GetRegistration(parameter.ParameterType) != null || this.CanBuildParameterExpression(parameter); }


    [DebuggerStepThrough]
    private bool CanBuildParameterExpression(ParameterInfo parameter)
    {
        try
        {
            this.container.Options.ConstructorInjectionBehavior
                .BuildParameterExpression(parameter);
            return true;
        }
        catch (ActivationException)
        {
            return false;
        }
    }






    [DebuggerStepThrough] private string BuildExceptionMessage(Type type) { if (!type.GetConstructors().Any()) { return string.Format(CultureInfo.InvariantCulture, "For the container to be able to create {0}, " + "it should contain at least one public " + "constructor.", type); } return string.Format(CultureInfo.InvariantCulture, "For the container to be able to create {0}, it " + "should contain a public constructor that " + "only contains parameters that can be resolved.", type); } }