This project is read-only.

How do I register a delegate with the non-generic Register method?

Jan 13, 2015 at 3:27 PM
I can do this:
var container = new Container();

container.Register<Service>();
container.RegisterSingle<Func<Service>>(() => 
    container.GetInstance(typeof(Service)) as Service);

var result = container.GetInstance<Func<Service>>()();
But not this:
var container = new Container();

container.Register<Service>();
container.RegisterSingle(typeof(Func<Service>), () => 
    container.GetInstance(typeof(Service)) as Service);

var result = container.GetInstance<Func<Service>>()();
The second block of code gives an error:
SimpleInjector.ActivationException: The registered delegate for type Func<Service> threw an exception. Unable to cast object of type 'Service' to type 'System.Func`1[ConsoleApplication1.Service]'.
Can you tell me the obvious thing I am missing?
Jan 13, 2015 at 3:54 PM
It took a moment to realize what went wrong, but you are basically doing this:
container.RegisterSingle(typeof(Func<Service>), container.GetInstance<Service>);
Which is basically the same as:
Func<Service> serviceFactory = container.GetInstance<Service>;
container.RegisterSingle(typeof(Func<Service>), serviceFactory);
With the RegisterSingle(Type, Func<object>) overload you register a Func<object> delegate that returns an instance of the type that is supplied as the first argument. Read this previous line again and now look closely at what you are registering.

Since the supplied type is of Func<Service> the second argument should be a delegate that returns a Func<Service>. However, the delegate you are registering returns Service.

So this will work:
Func<Service> serviceFactory = container.GetInstance<Service>;
Func<Func<Service>> serviceServiceFactory = () => serviceFactory;
container.RegisterSingle(typeof(Func<Service>), serviceServiceFactory);
Because you use the non-generic RegisterSingle overload that takes in a Func<object>, Simple Injector will not be able to check the correctness of your registration until the moment of verification or resolving.
Jan 16, 2015 at 1:24 PM
Edited Jan 16, 2015 at 1:30 PM
This is what I was trying to do:
private static void RegisterDelegate(Container container, Type service)
{
    var type = typeof(Func<>).MakeGenericType(service);
    var factory = GetExpressionToInvokeDelegate(
        GetContainerGetInstanceForType((container, service));
    container.RegisterSingle(type, () => factory);
}

private static Delegate GetExpressionToInvokeDelegate(Delegate d)
{
    var mi = d.GetType().GetMethod("Invoke");

    var call = Expression.Call(Expression.Constant(d), mi);

    return Expression.Lambda(call).Compile();
}

private static Delegate GetContainerGetInstanceForType(Container container, Type type)
{
    var mi = typeof(Container)
        .GetMethod("GetInstance", new Type[] { })
        .MakeGenericMethod(type);

    var call = Expression.Call(
        Expression.Constant(container),
        mi);

    var d = Expression.Lambda(call).Compile();

    return d;
}
Jan 16, 2015 at 1:38 PM
Wouldn't that be the same as this:
public static FactoryExtensions {
    public static void RegisterFactory<TService>(this Container container) {
        container.RegisterSingle<Func<TService>>(container.GetInstance<TService>);
    }

    public static void RegisterFactory(this Container container, Type serviceType) {
        var method = (
            from method in typeof(FactoryExtensions).GetMethods()
            where method.name == "RegisterFactory" && method.GetParameters().Length == 1
            select method).Single();

        method.MakeGenericMethod(serviceType).Invoke(null, container);
    }
}
But if you use this, you can also do this:
container.AllowResolvingFuncFactories();
Jan 16, 2015 at 2:04 PM
Sweet! I knew it was worth posting my attempt so you could show me a simple technique.

Thanks.
Jan 16, 2015 at 2:18 PM
Edited Jan 16, 2015 at 2:21 PM
And this is an optimized version:
public static void RegisterFactory<TService>(this Container container) {
    InstanceProducer producer = null;
    container.RegisterSingle<Func<TService>>(() => {
        if (producer == null) producer = container.GetRegistration(typeof(TService), true);
        return (TService)producer.GetInstance();
    });
}
Marked as answer by qujck on 1/16/2015 at 7:20 AM