Automatically generated factories from interface

Feb 22, 2014 at 9:23 PM
I am trying to migrate from Ninject to Simple Injector. One feature that I heavily use in Ninject is automated factories. Essentially it lets me create a factory interface without an explicit implemenation. The container creates the implementation for you. For example:
class SomeClass : ISomeClass
{
    public SomeClass(SomeObject someObject, ISomeDependency) { .. }
}

interface ISomeClassFactory {
    ISomeClass CreateSomeClass(SomeObject someObject);
}

// then in my IOC configuration
container.Bind<ISomeClass>().To<SomeClass>();
container.Register<ISomeClassFactory>().ToFactory();

// now any class that requires ISomeClassFactory will be able to call it and everything will be wired up automatically.
I understand that there may be ways to re-architect the code in order to make it work with Simple Injector but I am wondering if there's a way to get this to happen automatically like in Ninject so that I can have an easier time migrating it.

Regards
Coordinator
Feb 23, 2014 at 12:51 PM
This exact question was asked a few days ago at Stackoverflow: Factory Interface in Simple Injector. Using the extension method supplied in the answer, you can do the following:
container.Register<ISomeClass, SomeClass>();
container.RegisterFactory<ISomeClassFactory>();
Feb 23, 2014 at 1:17 PM
It doesn't seem the factory method provided in the Stack Overflow post passes in arguments that were specified in the factory method to the class being created. Is that correct? Is there an easy way to do that in Simple Injector?

Thanks
Coordinator
Feb 23, 2014 at 2:21 PM
The real question here is why you want to pass runtime arguments (your SomeObject) as constructor argument. Constructor arguments should be used for design/compose time values and method arguments should be used for runtime arguments. This way you can separate the building of the object graph from the runtime behavior of the application, which seems to be mixed in your case. Mixing this can lead to all sorts of annoying problems (as can be seen here, here and here). It also leads to configurations that are hard to verify.

So it would be best to either move that SomeObject to one of the methods on the ISomeClass interface, or allow the SomeObject to be requested by a service dependency that is injected. Say for instance you have some user information of the current request you want to pass into the class, in that case you can define an IUserContext interface with a CurrentUser property. The IUserContext can be injected into SomeClass, which can call the CurrentUser during execution (but not inside the constructor).

Still, passing in runtime arguments into the constructor is doable with Simple Injector (not easy, but doable), by implementing and registering a custom IConstructorInjectionBehavior, and setting some thread-local variables just before requesting the instance. I however rather see you fix the current design problems in your application.
Feb 26, 2014 at 10:00 PM
I'm wondering if it would be possible to do this by changing your StackOverflow example. Make it so the InvokeFactory resolves all the arguments that were not passed in using SimpleInjector but then creates the instance by itself using Activator.CreateInstance. Not sure if this is insane or would make it work w/o needing thread locals.
Developer
Feb 27, 2014 at 12:10 AM
I think the problem is that this would require to know the concrete type that was registered for the interface of the factory interface's method-return type (if that is an interface as in your first example). As soon as this type would be known an extension could be implemented to manually match the concrete-types ctor args with the IMethodMessage.Args of the proxy, resolve other types using the container and finally using Activator.CreateInstance() when the full set of ctor args is available to create the instance.

(@dot_NET_Junkie: btw this might also be a use-case for GetIntance<T>(context) ... to pass in missing or override existing types)
Feb 27, 2014 at 1:14 AM
Edited Feb 27, 2014 at 1:15 AM
blueling wrote:
I think the problem is that this would require to know the concrete type that was registered for the interface of the factory interface's method-return type (if that is an interface as in your first example). As soon as this type would be known an extension could be implemented to manually match the concrete-types ctor args with the IMethodMessage.Args of the proxy, resolve other types using the container and finally using Activator.CreateInstance() when the full set of ctor args is available to create the instance.

(@dot_NET_Junkie: btw this might also be a use-case for GetIntance<T>(context) ... to pass in missing or override existing types)
Can you give an example of what you mean by "an extension could be implemented to manually match the concrete-types ctor args" ? I was thinking you could just ask Simple Injector what the type is that was registered or have a way to register specific types for the factory.
Coordinator
Feb 27, 2014 at 1:59 PM
Edited Mar 2, 2014 at 6:29 PM
@blueling is right. There are two options here.
  1. The proxy can manually create the concrete type (using Activator.CreateInstance for instance) and do the auto-wiring itself by looking at the incoming arguments and resolving the rest from the container. To be able to do this it must know the concrete type, since Activator.CreateInstance can't be called with an interface.
  2. The proxy forwards the call to the container, asking for the return type of the method (the abstraction). In this case the container will auto-wire the concrete type, but this means that both the constructor verification behavior and constructor injection behavior must be overridden, and this new behavior must still know about the concrete type, since it must know when to do custom behavior and when to fallback to default behavior (which is for 99% of the types).
Advantage of option 1, is that it is relatively easy to implement, while option 2 is much more work.
Advantage of option 2 is that it integrates completely in the Simple Injector pipeline, allowing things like context based injection, property injection, initialization and verification on the factory products.

I created an example for option 2. Note that you -not only- have to register the factory, but also the product, as follows:
var container = new Container();
container.Options.EnableAutomaticParameterizedFactories();

// Always register the factory BEFORE the product.
container.RegisterFactory<ISomeClassFactory>();
container.RegisterFactoryProduct<ISomeClass, SomeClass>();
Here's the code:
using SimpleInjector.Advanced;
    
public static class AutomaticParameterizedFactoryExtensions
{
    public static void EnableAutomaticParameterizedFactories(this ContainerOptions options)
    {
        if (GetBehavior(options.Container) != null) 
            throw new InvalidOperationException("Already called.");

        var behavior = new AutomaticParameterizedFactoriesHelper(options);

        options.ConstructorInjectionBehavior = behavior;
        options.ConstructorVerificationBehavior = behavior;

        SetBehavior(options.Container, behavior);
    }

    public static void RegisterFactoryProduct<TConcrete>(this Container container,
        Lifestyle lifestyle = null)
        where TConcrete : class
    {
        RegisterFactoryProduct<TConcrete, TConcrete>(container, lifestyle);
    }

    public static void RegisterFactoryProduct<TService, TImplementation>(
        this Container container, Lifestyle lifestyle = null)
        where TImplementation : class, TService
        where TService : class
    {
        var behavior = GetBehavior(container);

        if (behavior == null) throw new InvalidOperationException(
            "Please call container.Options.EnableAutomaticParameterizedFactories() first.");

        behavior.RegisterFactoryProduct(typeof(TService), typeof(TImplementation));

        container.Register<TService, TImplementation>(lifestyle ?? Lifestyle.Transient);
    }

    public static void RegisterFactory<TFactory>(this Container container)
    {
        if (!typeof(TFactory).IsInterface)
        {
            throw new ArgumentException(typeof(TFactory).Name + " is no interface");
        }

        var parameters = (
            from method in typeof(TFactory).GetMethods()
            from parameter in method.GetParameters()
            select new { method.ReturnType, parameter.ParameterType })
            .ToList();

        if (parameters.Any())
        {
            var behavior = GetBehavior(container);

            if (behavior == null)
            {
                throw new InvalidOperationException(
                    "This factory contains parametrized methods. Please call " +
                    "container.Options.EnableAutomaticParameterizedFactories() first.");
            }

            parameters.ForEach(p => behavior.Register(p.ReturnType, p.ParameterType));
        }

        container.ResolveUnregisteredType += (s, e) =>
        {
            if (e.UnregisteredServiceType == typeof(TFactory))
            {
                var singletonFactory = Expression.Constant(
                    value: CreateFactory(typeof(TFactory), container),
                    type: typeof(TFactory));

                e.Register(singletonFactory);
            }
        };
    }

    private static object CreateFactory(Type factoryType, Container container)
    {
        var proxy = new AutomaticFactoryProxy(factoryType, container);

        return proxy.GetTransparentProxy();
    }

    private static AutomaticParameterizedFactoriesHelper GetBehavior(Container container)
    {
        return (AutomaticParameterizedFactoriesHelper)
            container.GetItem(typeof(AutomaticParameterizedFactoriesHelper));
    }

    private static void SetBehavior(Container container,
        AutomaticParameterizedFactoriesHelper behavior)
    {
        container.SetItem(typeof(AutomaticParameterizedFactoriesHelper), behavior);
    }

    private sealed class AutomaticFactoryProxy : RealProxy
    {
        private readonly Type factoryType;
        private readonly Container container;
        private readonly AutomaticParameterizedFactoriesHelper helper;

        [DebuggerStepThrough]
        public AutomaticFactoryProxy(Type factoryType, Container container)
            : base(factoryType)
        {
            this.factoryType = factoryType;
            this.container = container;
            this.helper = GetBehavior(container);
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage callMessage = msg as IMethodCallMessage;

            if (callMessage != null)
            {
                return this.InvokeFactory(callMessage);
            }

            return msg;
        }

        private IMessage InvokeFactory(IMethodCallMessage message)
        {
            if (message.MethodName == "GetType")
            {
                return new ReturnMessage(this.factoryType, null, 0, null, message);
            }

            if (message.MethodName == "ToString")
            {
                return new ReturnMessage(this.factoryType.FullName, null, 0, null, message);
            }

            var method = (MethodInfo)message.MethodBase;

            var parameters = CreateParameterValues(message);

            try
            {
                object instance = this.container.GetInstance(method.ReturnType);

                return new ReturnMessage(instance, null, 0, null, message);
            }
            finally
            {
                RestoreParameterValues(message, parameters);
            }
        }

        private ParameterValue[] CreateParameterValues(IMethodCallMessage message)
        {
            var method = (MethodInfo)message.MethodBase;

            var parameterValues = method.GetParameters()
                .Zip(message.Args, (p, v) => new ParameterValue(p, v)).ToArray();

            foreach (var p in parameterValues)
            {
                var local = this.helper.GetThreadLocal(method.ReturnType,
                    p.Parameter.ParameterType);
                p.OldValue = local.Value;
                local.Value = p.FactoryValue;
            }

            return parameterValues;
        }

        private void RestoreParameterValues(IMethodCallMessage message,ParameterValue[] values)
        {
            var method = (MethodInfo)message.MethodBase;

            foreach (var p in values)
            {
                var local = this.helper.GetThreadLocal(method.ReturnType, p.Parameter.ParameterType);
                local.Value = p.OldValue;
            }
        }
    }

    private sealed class ParameterValue
    {
        internal readonly ParameterInfo Parameter;
        internal readonly object FactoryValue;
        internal object OldValue;

        internal ParameterValue(ParameterInfo parameter, object factoryValue)
        {
            this.Parameter = parameter;
            this.FactoryValue = factoryValue;
        }
    }

    private sealed class AutomaticParameterizedFactoriesHelper
        : IConstructorVerificationBehavior, IConstructorInjectionBehavior
    {
        private Container container;
        private IConstructorVerificationBehavior originalVerificationBehavior;
        private IConstructorInjectionBehavior originalInjectionBehavior;
        private Dictionary<Type, Dictionary<Type, ThreadLocal<object>>> serviceLocals =
            new Dictionary<Type, Dictionary<Type, ThreadLocal<object>>>();
        private Dictionary<Type, Dictionary<Type, ThreadLocal<object>>> implementationLocals =
            new Dictionary<Type, Dictionary<Type, ThreadLocal<object>>>();

        public AutomaticParameterizedFactoriesHelper(ContainerOptions options)
        {
            this.container = options.Container;
            this.originalVerificationBehavior = options.ConstructorVerificationBehavior;
            this.originalInjectionBehavior = options.ConstructorInjectionBehavior;
        }

        // Called by RegisterFactory<TFactory>
        internal void Register(Type serviceType, Type parameterType)
        {
            Dictionary<Type, ThreadLocal<object>> parameterLocals;

            if (!this.serviceLocals.TryGetValue(serviceType, out parameterLocals))
            {
                this.serviceLocals[serviceType] =
                    parameterLocals = new Dictionary<Type, ThreadLocal<object>>();
            }

            parameterLocals[parameterType] = new ThreadLocal<object>();
        }

        // Called by RegisterFactoryProduct<T>
        internal void RegisterFactoryProduct(Type serviceType, Type implementationType)
        {
            // Create a mapping from implementationType to serviceType.
            if (this.serviceLocals.ContainsKey(implementationType))
            {
                this.implementationLocals[implementationType] =
                    this.serviceLocals[serviceType];
            }
        }

        void IConstructorVerificationBehavior.Verify(ParameterInfo parameter)
        {
            if (this.FindThreadLocal(parameter) == null)
            {
                this.originalVerificationBehavior.Verify(parameter);
            }
        }

        Expression IConstructorInjectionBehavior.BuildParameterExpression(
            ParameterInfo parameter)
        {
            var local = FindThreadLocal(parameter);

            if (local != null)
            {
                if (parameter.ParameterType.IsValueType && this.container.IsVerifying())
                {
                    throw new InvalidOperationException(
                        "You can't use Verify() is the factory product contains value types.");
                }

                return Expression.Convert(
                    Expression.Property(Expression.Constant(local), "Value"),
                    parameter.ParameterType);
            }

            return this.originalInjectionBehavior.BuildParameterExpression(parameter);
        }

        internal ThreadLocal<object> GetThreadLocal(Type serviceType, Type parameterType)
        {
            return this.serviceLocals[serviceType][parameterType];
        }

        private ThreadLocal<object> FindThreadLocal(ParameterInfo parameter)
        {
            Dictionary<Type, ThreadLocal<object>> parameterLocals;

            if (this.implementationLocals.TryGetValue(parameter.Member.DeclaringType, 
                out parameterLocals))
            {
                ThreadLocal<object> local;

                if (parameterLocals.TryGetValue(parameter.ParameterType, out local))
                {
                    return local;
                }
            }

            return null;
        }
    }
}
Marked as answer by dot_NET_Junkie on 3/2/2014 at 11:20 AM