使用 Unity 根据其消费者/上下文依赖项注入特定依赖项

Inject specific dependency based on its consumer / context dependency with Unity

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

我有 LoggingService class 它将 class 名称作为参数,return 是 NLog 的一个实例。为此,我使用了依赖注入。

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

代码:

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? */

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

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

您使用 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 的实现)并自动注册所有实例。

这看起来像是一个隐藏在 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);
    }
}

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

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

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

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