Как вызвать конструктор класса, который создается с помощью Structure Map в C#

У меня есть интерфейс под названием ILogger, который в основном содержит некоторые методы для регистрации.

Ilogger.cs

public interface ILogger
{   
    void LogError(string message, Exception exception = null);

    void LogMessage(string message);

    void LogValidationError(UploadResult uploadResult);

    void LogValidationError(ValidationResult validationResult);

    void LogProcessingError(string processingError);
}

У меня есть класс LogHelper, который реализует этот интерфейс. Класс LogHelper создается через StructureMap, как

ObjectFactory.Initialize(
    request =>
    {
        request.For<ILogger>().Singleton().Use<LogHelper>();

    });

У меня есть много классов, в конструкторе которых я просто создаю экземпляр этого класса и вызываю методы для регистрации информации. Например: у меня есть класс скажем Dummy1, в конструкторе которого я создаю экземпляр LogHelper как:

public Dummy1()
{
    this.logger = ObjectFactory.GetInstance<ILogger>();     
}

В LogHelper у меня есть метод, который в основном создает файл журнала и записывает сообщение, переданное в качестве параметра к нему.

public void LogMessage(string message)
{
    using (var writer = this.GetTextWriter(this.messageFilename))
    {
        writer.WriteLine(message);
    }
}

В настоящее время имя файла жестко закодировано в постоянное свойство класса LogHelper как private string messageFilename = "logs\\UserCreationResult.log";

Но я хочу, чтобы имя файла отправлялось динамически всякий раз, когда создается экземпляр LogHelper. Я думал о наличии свойства класса и определяю это свойство в конструкторе всякий раз, когда создается экземпляр класса. Но поскольку класс LogHelper создается как ObjectFactory.GetInstance<ILogger>(), Я не могу вызвать конструктор, в котором я могу передать имя файла.

2 ответа

Решение

К сожалению, то, как вы это делаете, немного самоубийственно. Ваши занятия знают только о ILogger, а не какая-то конкретная реализация ILogger, Это хорошо - это означает, что реализация может записывать в файл, таблицу SQL или что угодно.

Но если ваш класс знает только о ILogger, а не реализация, тогда как ваш класс узнает, что регистратору нужен путь к файлу? Если вы измените свой метод подписи в ILogger чтобы содержать путь к файлу, то происходят две вещи.

  1. Становится невозможным иметь какую-либо реализацию ILogger который не записывает в файл (если он не игнорирует путь к файлу, что было бы очень странно)
  2. Теперь тот класс, который вызывает регистратор, должен знать путь к файлу. Откуда этот класс получит путь к файлу? Будет ли оно храниться в классе? В этом случае вы получите класс, который не работает, если только он не является частью сборки, выполняющейся на компьютере, где он может записать в этот точный путь к файлу.

Вместо этого детали того, где и как войти, должны жить где-то в вашем ILogger реализация. Это ближе к принципу единой ответственности. Класс, который вызывает ILogger не несет ответственности за решения о том, как ILogger работает. Он не знает и не хочет знать. Там написано: "Вот, возьми и запиши". Реализация регистратора отвечает за все остальное.

Я бы порекомендовал слом статики ObjectFactory полностью и используя контейнер для разрешения и создания всех ваших классов, включая регистратор и классы, которые зависят от него, но это настолько широко, что это не очень полезно. (Это устарело, потому что это плохой шаблон. Это даже не в последней версии StructureMap.)

Все выше это рекомендация. После этого я предлагаю вариант, который на самом деле не рекомендуется, но требует меньше изменений и не дает вашим классам знать пути к файлам, потому что, пожалуйста, не делайте этого никогда.

Один из вариантов - на полпути компромисс - может быть регистрация различных именованных реализаций ILogger, Вы можете изменить свой класс логгера так, чтобы он выглядел так:

public class FileLogger : ILogger
{
    private readonly string _filePath;

    public FileLogger(string filePath)
    {
        _filePath = filePath;
    }
}

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

Тогда вы можете зарегистрировать свои реализации, как это.

ObjectFactory.Initialize(
    request =>
    {
        request.For<ILogger>().Singleton()
            .Use<FileLogger>(() => new FileLogger("some path")).Name = "LoggerOne";

        request.For<ILogger>().Singleton()
             .Use<FileLogger>(() => new FileLogger("some other path")).Name = "LoggerTwo";

    });

Теперь ваш класс может сказать, какой регистратор ему нужен, например так:

var logger = ObjectFactory.GetNamedInstance<ILogger>("LoggerOne");

Но, пожалуйста, тоже не делай этого. Это больше, чем я могу описать здесь очень подробно, но взгляните на внедрение зависимостей, чтобы ваши классы знали только о ILogger и не знают или не заботятся о том, какую реализацию они получают, и ничего не говорят ей о том, как выполнять свою работу.

Вы используете свой регистратор в качестве одиночного, поэтому вы не создаете экземпляр при каждом вызове ObjectFactory.GetInstance<ILogger>();вы просто получаете ссылку на один и тот же экземпляр регистратора, который создается один раз при первом использовании.

Если вы хотите написать в определенное место назначения, то лучшим решением будет указать место назначения в Logging методы:

 void LogError(string message, 
               Exception exception = null,
               string destination = /*some adequate defualt value*/);

void LogMessage(string message,
                string destination = /*some adequate defualt value*/);

Создание информации о состоянии в вашем экземпляре регистратора с определенным назначением может быть опасным, если вы используете регистратор одновременно с методами, которые ожидают и, следовательно, устанавливают разные пункты назначения; Вы можете в конечном итоге регистрировать вещи там, где они не должны.

Что поднимает важную проблему; поскольку вы используете логгер в своем приложении (singleton), убедитесь, что его методы безопасны для одновременного вызова, если есть вероятность, что он будет вызываться таким образом.

Другие вопросы по тегам