【问题标题】:Resolve generic Microsoft.Extensions.Logging.ILogger<T> with Unity - get InvalidCastException使用 Unity 解决通用 Microsoft.Extensions.Logging.ILogger<T> - 获取 InvalidCastException
【发布时间】:2019-04-08 12:03:52
【问题描述】:

我正在尝试在 Unity(版本 4)中注册通用 ILogger(来自 Microsoft.Extensions.Logging,而不是来自 Serilog)。

我有以下课程:

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:依赖项解析失败,类型 =“MyNamespace.MyClass”,名称 =“(无)”。 发生异常时:解析构造函数 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]' 的参数“logger”。

更新

使用 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

【问题讨论】:

  • container.RegisterType&lt;MyClass, MyClass&gt;(); 不应该类似于 container.RegisterType(); 吗?依赖注入通常是请求一个接口,而不是一个类。
  • container.RegisterType&lt;MyClass, MyClass&gt;() 用于演示,问题是 ILogger&lt;MyClass&gt; 解决方案。
  • 您是否尝试使用interface 注册到class?解决可能有问题,因为它没有接口..
  • 尝试使用 private readonly Microsoft.Extensions.Logging.ILogger _logger;public MyClass(Microsoft.Extensions.Logging.ILogger logger)。您正在注入配置中创建一个非专业记录器,因此当然不能分配给专门的记录器。
  • @kara 是的,接口也存在同样的问题(例如 IMyClass)

标签: c# unity-container serilog .net-core-logging


【解决方案1】:

我找到的最简单的方法是注册一个工厂并反映他们的扩展方法来做到这一点并获得正确的响应类型。此示例还要求您已注册 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】:

    由于已经有一个 Logger 实现将 ILoggerFactory 作为依赖项,因此您可以这样做

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

    【讨论】:

      【解决方案3】:

      我在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>();
      
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-30
          • 1970-01-01
          • 2011-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-10
          相关资源
          最近更新 更多