【问题标题】:Inject specific dependency based on its consumer / context dependency with Unity使用 Unity 根据其使用者/上下文依赖项注入特定依赖项
【发布时间】:2018-10-04 20:23:50
【问题描述】:

我在我的 Web API 项目中使用 Nlog 作为日志框架,并带有 Unity 一个 IoC 容器。

我有LoggingService 类,它将类名作为参数并返回一个 NLog 实例。为此,我使用了依赖注入。

问题:我很困惑如何将类名传递给我的LoggingService 类?

代码:

using NLog;

namespace Portal.Util.Logging
{
    public class LoggingService : ILoggingService
    {
        private readonly ILogger _logger;

        public LoggingService(string currentClassName)
        {
            _logger = LogManager.GetLogger(currentClassName);
        }

        public void FirstLevelServiceLog(string log)
        {
            _logger.Log(LogLevel.Debug, log);
        }
    }

    public interface ILoggingService
    {
        void FirstLevelServiceLog(string log);
    }
}

服务层:(这是从控制器调用的)

public class MyService : IMyService
{
    private readonly ILoggingService _loggingService;

    public MyService(ILoggingService loggingService)
    {
        _loggingService = loggingService
    }

    public DoSomething()
    {
        _loggingService.FirstLevelServiceLog("Debug");
    }
}

团结:

var container = new UnityContainer();
container.RegisterType<ILoggingService, LoggingService>(new InjectionConstructor("")) 
/* Not sure on how to pass the class name here? */

【问题讨论】:

    标签: asp.net-web-api dependency-injection unity-container nlog


    【解决方案1】:

    这看起来像是隐藏在XY problem 后面的设计问题。

    日志服务需要进行一些重构,以便更轻松地注入所需的行为。

    引入一个额外的泛型接口,它派生自基础ILoggingService

    public interface ILoggingService<TType> : ILoggingService {
    
    }
    
    public interface ILoggingService {
        void FirstLevelServiceLog(string log);
    }
    

    重构当前实现以依赖于服务的通用版本

    public class LoggingService<TType> : ILoggingService<TType> {
        private readonly ILogger _logger;
    
        public LoggingService() {
            string currentClassName = typeof(TType).Name;
            _logger = LogManager.GetLogger(currentClassName);
        }
    
        public void FirstLevelServiceLog(string log) {
            _logger.Log(LogLevel.Debug, log);
        }
    }
    

    这将允许使用类型参数来确定日志管理器的类的名称

    现在那些依赖于日志服务的人可以通过构造函数注入来明确他们的依赖

    public class MyService : IMyService {
        private readonly ILoggingService _loggingService;
    
        public MyService(ILoggingService<MyService> loggingService) {
            _loggingService = loggingService
        }
    
        public DoSomething() {
            _loggingService.FirstLevelServiceLog("Debug");
        }
    }
    

    请注意,唯一需要重构的是构造函数的泛型参数,因为泛型记录器派生自基础 ILoggingService

    日志服务抽象最终可以使用开放泛型注册实现

    var container = new UnityContainer();    
    container.RegisterType(typeof(ILoggingService<>), typeof(LoggingService<>));
    

    这样就无需为系统中使用的每个记录器注册单独的实现。

    【讨论】:

      【解决方案2】:

      一种方法是使用按名称注册。

      您首先注册所有记录器并为每个记录器命名。

      您使用InjectionConstructor 来注入参数值。

      var ctr = new UnityContainer();
      
      // You can add lifetimemanagers as well
      ctr.RegisterType<ILoggingService, LoggingService>("Class1Logger",new InjectionConstructor("Class1"));
      ctr.RegisterType<ILoggingService, LoggingService>("Class2Logger", new InjectionConstructor("Class2"));
      

      然后通过按名称注入来创建逻辑类(使用Dependency 属性):

      public class MyLogic1
      {
          public MyLogic1([Dependency("Class1Logger")]ILoggingService logger)
          {
      
          }
      }
      
      public class MyLogic2
      {
          public MyLogic2([Dependency("Class2Logger")]ILoggingService logger)
          {
      
          }
      }
      

      这就是你需要做的所有事情。

      我通常认为按名称注册并不是最佳实践,而且我大多数时候更喜欢某种虚拟接口继承(因为我不喜欢将相同的接口类型映射到具体类型)。 但在这种情况下,它看起来像是解决问题的一种体面且干净的方法。

      如果您想要自动注册,您可以使用某种反射(自定义属性或基于ILogger 的实现)并自动注册所有实例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-02
        • 1970-01-01
        • 2011-11-22
        • 2015-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-06
        相关资源
        最近更新 更多