使用 Unity 解决泛型 Microsoft.Extensions.Logging.ILogger<T> - 获取 InvalidCastException
Resolve generic Microsoft.Extensions.Logging.ILogger<T> with Unity - get InvalidCastException
我正在尝试在 Unity(版本 4)中注册通用 ILogger(来自 Microsoft.Extensions.Logging,而不是来自 Serilog)。
我有以下 class:
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>();
这对我来说似乎没问题,太糟糕了,我在解决时遇到以下错误:
Exception is: InvalidCastException - Unable to cast object of type 'Serilog.Extensions.Logging.SerilogLogger' to type 'Microsoft.Extensions.Logging.ILogger`1[MyNamespace.MyClass]'.
如何解决这个问题?
完全异常:
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.ILogger
1[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
不幸的是,Unity 没有 AddSerilog
,我必须使用 Unity
我借助 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>();
我发现的最简单的方法是注册一个工厂并反映他们的扩展方法来做到这一点并获得正确的响应类型。此示例还期望您已注册一个 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 });
});
因为已经有一个将 ILoggerFactory 作为依赖项的 Logger 实现,您可以这样做
container.RegisterType<ILoggerFactory, MyLoggerFactory>();
container.RegisterType(typeof(ILogger<>), typeof(Logger<>));
container.Resolve<ILogger<MyClass>>();
Unity.Microsoft.Logging has a LoggingExtension class 这使得实现起来相当简单:
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));
我正在尝试在 Unity(版本 4)中注册通用 ILogger(来自 Microsoft.Extensions.Logging,而不是来自 Serilog)。
我有以下 class:
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>();
这对我来说似乎没问题,太糟糕了,我在解决时遇到以下错误:
Exception is: InvalidCastException - Unable to cast object of type 'Serilog.Extensions.Logging.SerilogLogger' to type 'Microsoft.Extensions.Logging.ILogger`1[MyNamespace.MyClass]'.
如何解决这个问题?
完全异常:
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.ILogger
1[[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.ILogger
1[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
不幸的是,Unity 没有 AddSerilog
,我必须使用 Unity
我借助 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>();
我发现的最简单的方法是注册一个工厂并反映他们的扩展方法来做到这一点并获得正确的响应类型。此示例还期望您已注册一个 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 });
});
因为已经有一个将 ILoggerFactory 作为依赖项的 Logger
container.RegisterType<ILoggerFactory, MyLoggerFactory>();
container.RegisterType(typeof(ILogger<>), typeof(Logger<>));
container.Resolve<ILogger<MyClass>>();
Unity.Microsoft.Logging has a LoggingExtension class 这使得实现起来相当简单:
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));