Внедрить конкретную зависимость на основе зависимости от потребителя / контекста в Unity - PullRequest
0 голосов
/ 04 октября 2018

Я использую Nlog в качестве каркаса ведения журнала в моем проекте Web API с 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");
    }
}

Unity:

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

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Это похоже на проблему дизайна, скрытую за XY проблемой .

Служба ведения журналов нуждается в некотором рефакторинге, чтобы упростить внедрение желаемого поведения.

представляет дополнительный универсальный интерфейс, производный от базового 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<>));

Таким образом, нет необходимости регистрировать отдельную реализацию для каждойрегистратор, используемый в системе.

0 голосов
/ 07 октября 2018

Один из способов сделать это - использовать регистрацию по имени.

Сначала вы регистрируете все свои регистраторы и присваиваете каждому регистратору его имя.

Вы используете 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) изарегистрируйте все свои экземпляры автоматически.

...