Inject creation delegates or Constructors

Oct 9, 2009 at 1:01 PM
Edited Oct 9, 2009 at 9:34 PM

I had the following problem:

Lets say I have a Consumer class, which needs to create multiple instances of the model class in order to do its work, I can obviously not just inject a dependency of IModel, but some delegate that supplies the Consumer with as many new Models as it requires.

I want to keep the container out of my classes, so I need a way to register and inject delegates.

I would like something like below without having to create any factory classes (e.g. ModelNew):

internal class Consumer : IConsumer
    {
        IModel _model1;

        IModel _model2;

        public Consumer(IModelNew newModel)
        {
            _model1 = newModel.Create();
            _model2 = newModel.Create();
        }

}

For now I found a workaround, injecting Func<IModel> and registering a creation delegate for that type with the container as so:

container
                .RegisterType<IModel, Model>()
                .RegisterInstance<Func<IModel>>(() => _container.Resolve<IModel>())
                .RegisterType<IConsumer, Consumer>();

Func<IModel> then gets injected into this consumer class:

 

internal class Consumer : IConsumer
    {
        IModel _model1;

        IModel _model2;

        public Consumer(Func<IModel> newModel)
        {
            _model1 = newModel.Invoke();
            _model2 = newModel.Invoke();
        }

}

To hide the plumbing somewhat, I created the following two extension methods:

 

namespace Microsoft.Practices.Unity
{
    using System;

    public static class UnityExtensions
    {
        #region Public Methods

        /// <summary>
        /// Registers the default constructor for the given concrete type as
        /// <code><![CDATA[IConstructor<TInterface>]]></code>
        /// with the container.
        /// </summary>
        /// <example>
        /// Assuming consumer2 needs to create objects of type IModel
        /// <code>
        /// <![CDATA[_container
        ///       .RegisterConstructor<IModel, Model>()
        ///        .RegisterType<IConsumer, Consumer2>();
        /// var consumer = _container.Resolve<IConsumer>();]]>
        /// </code>
        /// </example>
        /// <typeparam name="TInterface">Interface that represents type</typeparam>
        /// <typeparam name="TConcreteType">Concrete type to be constructed</typeparam>
        /// <param name="container"></param>
        /// <returns>IUnityContainer with registered constructor mapping</returns>
        public static IUnityContainer RegisterConstructor<TInterface, TConcreteType>(this IUnityContainer container)
            where TConcreteType : TInterface, new()
        {
            if (container != null)
            {
               container.RegisterInstance<IConstructor<TInterface>>(new Constructor<TInterface>(() => new TConcreteType()));
            }
            else
            {
                throw new ArgumentNullException("container");
            }

            return container;
        }

        /// <summary>
        /// Registers the default constructor for the given concrete type as
        /// <code><![CDATA[IConstructor<TInterface>]]></code>
        /// with the container.
        /// It also performs
        /// <code><![CDATA[RegisterType<TInterface, TConcreteType>()]]></code>
        /// on the container.
        /// </summary>
        /// <example>
        /// Assuming consumer2 needs to create objects of type IModel
        /// <code>
        /// <![CDATA[_container
        ///       .RegisterConstructor<IModel, Model>()
        ///        .RegisterType<IConsumer, Consumer2>();
        /// var consumer = _container.Resolve<IConsumer>();]]>
        /// </code>
        /// </example>
        /// <typeparam name="TInterface">Interface that represents type</typeparam>
        /// <typeparam name="TConcreteType">Concrete type to be constructed</typeparam>
        /// <param name="container"></param>
        /// <returns>IUnityContainer with registered typemapping and constructor</returns>
        public static IUnityContainer RegisterTypeAndConstructor<TInterface, TConcreteType>(this IUnityContainer container)
            where TConcreteType : TInterface, new()
        {
            if (container != null)
            {
                container
                   .RegisterType<TInterface, TConcreteType>()
                   .Configure<InjectedMembers>()
                   .ConfigureInjectionFor<TConcreteType>(new InjectionConstructor())
                   .Container
                   .RegisterInstance<IConstructor<TInterface>>(new Constructor<TInterface>(container.Resolve<TInterface>));
            }
            else
            {
                throw new ArgumentNullException("container");
            }

            return container;
        }

        #endregion
    }

/// <summary>
    /// Acts as function wrapper which delegates to the default constructor of concrete type for the given interface
    /// </summary>
    /// <typeparam name="TInterface">The interface used to substitute for the concrete type</typeparam>
    public interface IConstructor<TInterface> : IFluentInterface
    {
        TInterface New { get; }
    }

public class Constructor<TInterface> : IConstructor<TInterface>
    {
        readonly Func<TInterface> _constructionDelegate;

       /// <summary>
       /// Receives a delegate, the points to the code which constructs the concrete type used for the interface.
       /// </summary>
       /// <param name="constructionDelegate">Points at the default constructor of the concrete type</param>
        public Constructor(Func<TInterface> constructionDelegate)
        {
            _constructionDelegate = constructionDelegate;
        }

        public TInterface New
        {
            get { return _constructionDelegate.Invoke(); }
        }
    }
}

 

As you can see, the RegisterConstructor method, doesn't register the type itself with the container, but just provides a delegate to create an instance of the passed in type.

RegisterTypeAndConstructor on the other hand registers the concrete type first with the container (specifying the default constructor as the InjectionConstructor) and then actually uses the Resolve of the container to provide an instance to the delegate of IConstructor.

The constructor would be injected and can be used to create new instances as so:

 

 
public Consumer(IConstructor<IModel> model) 
{
     IModel newModel = model.New; 
} 

 

So maybe this (or another implementation) could become part of Unity, so others can benefit?

Is there a way I could contribute?

Thanks,

Thorsten Lorenz