Factory for generic interfaces with Autofac

autofac, c#, generics

Solution

I found out a simple answer, but only for the `ILogger<T>` from the `Microsoft.Extensions.Logging` package: using the `ServiceCollection` from the `Microsoft.Extensions.DependencyInjection` package:

        IServiceCollection services = new ServiceCollection();
        services.AddLogging(); // all the magic happens here
        // set up other services 
        var builder = new ContainerBuilder();
        builder.Populate(services); // from the Autofac.Extensions.DependencyInjection package
        IContainer container = builder.Build();
        Test t = container.Resolve<Test>();

Problem

I have an `ILoggerFactory` implementation registered in Autofac container. I want to be able to register the following class with Autofac and be able to instantiate it: ``` public class Test { public Test(ILogger<Test> logger) { logger.LogInformation("Works!"); } } ``` For that I need to be able to 'teach' Autofac to produce instances implementing the `ILogger<T>` for any class `T`. There are an `ILoggerFactory` extension methods `CreateLogger<T>()` and `CreateLogger(Type)` that are able to produce instances of required type, but I can't wrap my head around how to make Autofac to call them. If I wanted non-generic `ILogger`, I'd write ``` containerBuilder.Register(c => c.Resolve<ILoggerFactory>().CreateLogger("default")) ``` but I'm not sure what to do for the generic one. Any pointers? Update: After reading some more documentation, I came up with this: ``` public class LoggingModule: Autofac.Module { private readonly MethodInfo mi; public LoggingModule() { this.mi = typeof(LoggerFactoryExtensions) .GetMethod(nameof(LoggerFactoryExtensions.CreateLogger), new[] { typeof(ILoggerFactory) }); } protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) { registration.Preparing += OnComponentPreparing; } private void OnComponentPreparing(object sender, PreparingEventArgs e) { e.Parameters = e.Parameters.Union( new[] { new ResolvedParameter( (p, i) => IsGenericLoggerParameter(p.ParameterType), (p, i) => CreateLogger(this.mi, p.ParameterType, i.Resolve<ILoggerFactory>()) ) }); } private static bool IsGenericLoggerParameter(Type parameterType) { return parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(ILogger<>); } private static object CreateLogger(MethodInfo mi, Type typeArg, ILoggerFactory loggerFactory) { Type genericArg = typeArg.GetGenericArguments().First(); MethodInfo generic = mi.MakeGenericMethod(new[] { genericArg }); return generic.Invoke(null, new[] { loggerFactory }); } } ``` And then: ``` containerBuilder.RegisterType<Test>().AsSelf(); containerBuilder.RegisterModule<LoggingModule>(); var container = containerBuilder.Build(); var test = container.Resolve<Test>(); ``` works. Issue is I don't fully understand how it works (and, more importantly, how it might break), so I'm still looking for more elegant and/or understandable solutions.

Original source