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.
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>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With