Question

I am trying to inject a delegate with open generic through constructor like this

protected AQuery(Func<string, IQuery<T>> getQuery)
{
    query = getQuery("contextName");
}

And Register something like

builder
   .Register<Func<string, IQuery<T>>>(
       c => ctx => 
       new Query(c.ResolveNamed<IDbContext>(ctx)));

I could not find any API help documentation for something like this.

I was able to use similar registration where generics are not involved.

Was it helpful?

Solution

What you're trying to achieve seems a very functional approach that works great in functional languages like F#, but most DI containers for .NET are built for object oriented languages and they are inherently type oriented, not delegate or function oriented. What you are trying to do, can't be done easily with Autofac, or any other DI container for that matter.

And even if Autofac was able to map generic methods to Func delegates, you still needed a fair amount of reflection to register this. For instance this hypothetical RegisterGenericFunc method:

builder.RegisterGenericFunc(
    typeof(Func<,>).MakeGenericType(typeof(string), typeof(IQuery<>)),
    typeof(Bootstrapper).GetMethod("QueryFuncFactory"));

public static Func<string, IQuery<T>> QueryFuncFactory<T>(IComponentContext c) {
    return ctx => new Query(c.ResolveNamed<IDbContext>(ctx)));
}

For this to work though, Autofac must not only be able to work with delegates, but also be able to understand partial open-generic types (your Func<string, IQuery<T>> is partial open-generic).

So instead of doing this, you might want to fall back to a more object oriented approach and define a clear interface that describes your abstraction:

public interface IQueryFactory<T> {
     IQuery<T> CreateQuery(string context);
}

And your implementation could look like this:

public class AutofacQueryFactory<T> : IQueryFactory<T> {
     private readonly IComponentContext c;

     public AutofacQueryFactory(IComponentContext c) {
         this.c= c;
     }

     public IQuery<T> CreateQuery(string context) {
         return new Query<T>(c.ResolveNamed<IDbContext>(context));
     }
}

This interface-implementation pair can be registered as follows:

build.RegisterGeneric(typeof(AutofacQueryFactory<>)).As(typeof(IQueryFactory<>);

And your consumers can depend on IQueryFactory<T>:

protected AQuery(IQueryFactory<T> getQuery)
{
    query = getQuery.CreateQuery("contextName");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top