Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofac. How to inject a open Generic Delegate in constructor

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.

like image 726
hustler Avatar asked Dec 15 '22 00:12

hustler


1 Answers

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");
}
like image 92
Steven Avatar answered Feb 19 '23 15:02

Steven