Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to register an open generic delegate in autofac?

I want to register a generic delegate that resolves itself at runtime, but I cannot find a way to do this on generics.

Given a delegate that looks like this:

public delegate TOutput Pipe<in TInput, out TOutput>(TInput input);

And given a discretely registered delegate that look like this:

public class AnonymousPipe<TInput, TOutput>
{
   public Pipe<TInput, TOutput> GetPipe(IContext context)
   {...}

I want to register a function along the lines of this:

builder.RegisterGeneric(typeof(Pipe<,>)).As(ctx => 
{
   var typeArray = ctx.RequestedType.GetGenericArguments();
   // this can be memoized 
   var pipeDefinition = ctx.Resolve(typeof(AnonymousPipe<,>).MakeGenericType(typeArray));

   return pipeDefinition.GetPipe(ctx);

I cannot find a way to provide an implementation of the generic as a parameter in Autofac - I may just be missing something. I know I can do this through a generic object or interface, but I want to stick with the lightness of a delegate. It makes unit testing super simple on the injection of these.

Any thoughts? I am having to do discrete registrations at the moment(one per type combination and no generics).

like image 414
S. Hebert Avatar asked Nov 02 '11 17:11

S. Hebert


1 Answers

I can only come up with the registration source solution (the universal hammer in Autofac.)

class PipeSource : IRegistrationSource
{
    public bool IsAdapterForIndividualComponents { get { return true; } }

    public IEnumerable<IComponentRegistration> RegistrationsFor(
        Service service,
        Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        var swt = service as IServiceWithType;
        if (swt == null || !swt.ServiceType.IsGenericType)
            yield break;

        var def = swt.ServiceType.GetGenericTypeDefinition();
        if (def != typeof(Pipe<,>))
            yield break;

        var anonPipeService = swt.ChangeType(
            typeof(AnonymousPipe<,>).MakeGenericType(
                swt.ServiceType.GetGenericArguments()));

        var getPipeMethod = anonPipeService.ServiceType.GetMethod("GetPipe");

        foreach (var anonPipeReg in registrationAccessor(anonPipeService))
        {
            yield return RegistrationBuilder.ForDelegate((c, p) => {
                    var anon = c.ResolveComponent(anonPipeReg, p);
                    return getPipeMethod.Invoke(anon, null); })
                .As(service)
                .Targeting(anonPipeReg)
                .CreateRegistration();
        }
    }
}

Then:

builder.RegisterSource(new PipeSource());

Now, I'm certain that I can't type that code into a web page and have it actually compile and run, but it might come close :)

like image 191
Nicholas Blumhardt Avatar answered Oct 14 '22 11:10

Nicholas Blumhardt