Разрешите универсальный Microsoft.Extensions.Logging.ILogger<T> с Unity - получите InvalidCastException

Я пытаюсь зарегистрировать универсальный ILogger (из Microsoft.Extensions.Logging, а не из Serilog) в Unity (версия 4).

У меня есть следующий класс:

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

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

И следующие Unity регистрации и тестирование:

// 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>();

Мне кажется, это нормально, очень плохо, что при разрешении возникает следующая ошибка:

Исключением является: InvalidCastException - Невозможно привести объект типа 'Serilog.Extensions.Logging.SerilogLogger' к типу 'Microsoft.Extensions.Logging.ILogger`1[MyNamespace.MyClass]'.

Как исправить эту проблему?

Полное исключение:

Microsoft.Practices.Unity.ResolutionFailedException: не удалось разрешить зависимость, type = "MyNamespace.MyClass", name = "(none)". Исключительная ситуация во время: разрешения параметра "logger" конструктора 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]".

Обновить

при использовании Microsoft.Extensions.DependencyInjection это работает:

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

var serviceProvider = serviceCollection.BuildServiceProvider();

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

Поэтому я пытаюсь заставить это работать с Unity вместо Microsoft.Extensions.DependencyInjection

К сожалению, нет AddSerilog для единства, и я связан с единством

4 ответа

Самый простой способ, который я нашел, - это зарегистрировать фабрику и отразить ее метод расширения, чтобы сделать это и получить правильный тип ответа. В этом примере также ожидается, что вы зарегистрировали 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 });
        });

В Unity.Microsoft.Logging есть класс LoggingExtension , который делает это довольно просто:

      var loggerProvider = new SerilogLoggerProvider();
var loggerFactory = LoggerFactory.Create((builder) => builder.AddProvider(loggerProvider));
var container = new UnityContainer();
container.AddExtension(new LoggingExtension(loggerFactory));

Теперь должно работать любое из следующих действий:

      container.Resolve<ILogger<MyClass>>();
container.Resolve<ILogger>(nameof(MyClass));

Поскольку уже существует реализация Logger, которая принимает ILoggerFactory в качестве зависимости, вы можете просто сделать

container.RegisterType<ILoggerFactory, MyLoggerFactory>();
container.RegisterType(typeof(ILogger<>), typeof(Logger<>));
container.Resolve<ILogger<MyClass>>();

Я решил это с помощью MakeGenericMethod и вспомогательный метод:

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

И окончательный код (без кеширования и т. Д.):

// 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>();

Другие вопросы по тегам