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

橙三吉。 提交于 2020-06-15 08:28:57

问题


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


回答1:


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 });
        });



回答2:


I resolved this with the help of MakeGenericMethod and a helper method:

private static ILogger<T> CreateLogger<T>(ILoggerFactory factory)
{
    return new Logger<T>(factory);
}

And the final code (without reflection caching etc):

// Arrange
IUnityContainer container = new UnityContainer();

var logfactory = new LoggerFactory();

var provider = new Serilog.Extensions.Logging.SerilogLoggerProvider();
logfactory.AddProvider(provider);

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

        var myType = this.GetType();

        var createLoggerMethod1 = myType.GetMethod(nameof(CreateLogger),BindingFlags.Static | BindingFlags.NonPublic);

        var createLoggerMethod = createLoggerMethod1.MakeGenericMethod(loggerType);

        var logger = createLoggerMethod.Invoke(this, new object[] {logfactory});

        return logger;
    }));

container.RegisterType<MyClass, MyClass>();

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



来源:https://stackoverflow.com/questions/55573065/resolve-generic-microsoft-extensions-logging-iloggert-with-unity-get-invalid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!