Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolve generic Microsoft.Extensions.Logging.ILogger<T> with Unity - get InvalidCastException

I am trying to register a generic ILogger (from Microsoft.Extensions.Logging, not from Serilog) in Unity (version 4).

I've got the following class:

public class MyClass
{
    private readonly Microsoft.Extensions.Logging.ILogger<MyClass> _logger;

    public MyClass(Microsoft.Extensions.Logging.ILogger<MyClass> logger)
    {
        _logger = logger;
    }
}

And the following Unity registrations and test:

// Arrange
IUnityContainer container = new UnityContainer();

// provider from Serilog.Extensions.Logging nuget package
var provider = new Serilog.Extensions.Logging.SerilogLoggerProvider();

container.RegisterType(typeof(Microsoft.Extensions.Logging.ILogger<>),
    new InjectionFactory((ctr, type, name) =>
    {
        var loggerType = type.GetGenericArguments()[0].FullName;
        Microsoft.Extensions.Logging.ILogger logger = provider.CreateLogger(loggerType);
        return logger;
    }));

container.RegisterType<MyClass, MyClass>(); // just for demo purpose. 

// Assert
container.Resolve<MyClass>();

This seems OK to me, too bad I am having the following error when resolving:

Exception is: InvalidCastException - Unable to cast object of type 'Serilog.Extensions.Logging.SerilogLogger' to type 'Microsoft.Extensions.Logging.ILogger`1[MyNamespace.MyClass]'.

How to fix this issue?

Full exception:

Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "MyNamespace.MyClass", name = "(none)". Exception occurred while: Resolving parameter "logger" of constructor MyNamespace.MyClass(Microsoft.Extensions.Logging.ILogger1[[MyNamespace.MyClass, MyNamespace.Infra.UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=edbaf8e9a324553f]] logger). Exception is: InvalidCastException - Unable to cast object of type 'Serilog.Extensions.Logging.SerilogLogger' to type 'Microsoft.Extensions.Logging.ILogger1[MyNamespace.MyClass]'.

Update

when using Microsoft.Extensions.DependencyInjection, this works:

// Arrange
var serviceCollection = new Microsoft.Extensions.DependencyInjection.ServiceCollection()
    .AddLogging(builder => builder.AddSerilog())
    .AddTransient<MyClass, MyClass>();

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
serviceProvider.GetService<MyClass>();

So I try to get this working with Unity instead of Microsoft.Extensions.DependencyInjection

Unfortunately there is no AddSerilog for Unity and I'm bound to Unity

like image 703
Julian Avatar asked Apr 08 '19 12:04

Julian


1 Answers

The easiest way I have found is to register a factory and reflect their extension method to do it and get the right response type. This example also expects that you have registered an ILoggerFactory.

_container = new UnityContainer();
_container.RegisterInstance<ILoggerFactory>(new LoggerFactory().AddLog4Net(), new ContainerControlledLifetimeManager());
_container.RegisterFactory(typeof(ILogger<>), null, (c, t, n) =>
        {
            var factory = c.Resolve<ILoggerFactory>();
            var genericType = t.GetGenericArguments().First();
            var mi = typeof(Microsoft.Extensions.Logging.LoggerFactoryExtensions).GetMethods().Single(m => m.Name == "CreateLogger" && m.IsGenericMethodDefinition);
            var gi = mi.MakeGenericMethod(t.GetGenericArguments().First());
            return gi.Invoke(null, new[] { factory });
        });
like image 156
Paul Aicher Avatar answered Sep 19 '22 01:09

Paul Aicher