Способы сохранения кода конфигурации из логического кода с использованием Injection Dependency

Как сохранить код кода конфигурации из моего логического кода с помощью настроек (ApplicationSettingsBase) и Injection Dependency?

С конфигурацией я имею в виду конфигурационный файл, специфичный для клиента.

Мне действительно нужно вводить класс конфигурации каждый раз, когда он мне нужен, или есть другой шаблон?

Было бы здорово получить образец кода!

Примеры:

Статическая конфигурация:

public static class StaticConfiguration
{
    public static bool ShouldApplySpecialLogic { get; set; }
    public static string SupportedFileMask { get; set; }
}

public class ConsumerOfStaticConfiguration
{
    public void Process()
    {
        if (StaticConfiguration.ShouldApplySpecialLogic)
        {
            var strings = StaticConfiguration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Нестатическая конфигурация:

public interface IConfiguration
{
    bool ShouldApplySpecialLogic { get; set; }
    string SupportedFileMask { get; set; }
}

public class Configuration : IConfiguration
{
    public bool ShouldApplySpecialLogic { get; set; }
    public string SupportedFileMask { get; set; }
}

public class Consumer
{
    private readonly IConfiguration _configuration;

    public Consumer(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void Process()
    {
        if (_configuration.ShouldApplySpecialLogic)
        {
            var strings = _configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Статический контекст с нестатической конфигурацией:

public static class Context
{
    public static IConfiguration Configuration { get; set; }
}

public class ConsumerOfStaticContext
{
    public void Process()
    {
        if (Context.Configuration.ShouldApplySpecialLogic)
        {
            var strings = Context.Configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Ответ 1

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

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

Если вы все еще хотите прочитать значения из файла конфигурации, вы можете сделать это из приложения Root of Composition. С StructureMap он может выглядеть примерно так:

var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");

container.Configure(r => r
    .For<Consumer>()
    .Ctor<MyConfigurationSection>()
    .Is(config));

Ответ 2

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

Он также связывает ваш класс с знаниями инфраструктуры: детали, такие как "эти значения настроены вместе", выходят из конфигурации приложения и в ваши классы, увеличивая площадь поверхности, на которую влияют изменения в несвязанных системах.

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

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

Рассмотрите обработку int, string и bool, как и любая другая зависимость. Это сделает ваши классы более чистыми, более сфокусированными, более устойчивыми к изменениям и проще unit test.

Ответ 3

Я написал сообщение в блоге, объясняющее, как и почему мы используем StructureMap, чтобы наша конфигурация была отделена от нашей логики: http://lostechies.com/joshuaflanagan/2009/07/13/how-we-handle-application-configuration/

Функциональность, описанная в этом сообщении, теперь доступна в библиотеке утилиты FubuCore (вы можете получить ее через nuget): https://github.com/DarthFubuMVC/fubucore/tree/master/src/FubuCore/Configuration

Ответ 4

Один из способов - ввести интерфейс конфигурации, как вы публикуете. Вот пара других способов.

Отображение сеттера

class Consumer
{
    public bool ShouldApplySpecialLogic { get; set; }

    ...
}

В корне композиции вы можете прочитать конфигурационный файл или его hardcode. Пример Autofac:

builder.RegisterType<Consumer>().AsSelf()
    .OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);

Это, вероятно, целесообразно только тогда, когда у вас есть хороший по умолчанию

Инъекция конструктора

public class Server
{
    public Server(int portToListenOn) { ... }
}

В корне композиции:

builder.Register(c => new Server(12345)).AsSelf();

Ответ 5

В моих приложениях я делаю то, что вы сделали выше, с IoC. То есть, имея мой контейнер IoC (StructureMap), введите IApplicationSettings в мои классы.

Например, в проекте ASP.NET MVC3 это может выглядеть так:

Public Class MyController
    Inherits Controller

    ...
    Private ReadOnly mApplicationSettings As IApplicationSettings

    Public Sub New(..., applicationSettings As IApplicationSettings)
        ...
        Me.mApplicationSettings = applicationSettings
    End Sub

    Public Function SomeAction(custId As Guid) As ActionResult
         ...

         ' Look up setting for custId
         ' If not found fall back on default like
         viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue

         Return View("...", viewModel)
    End Function
End Class

Моя реализация IApplicationSettings извлекает большинство из файла приложения .config и также содержит несколько жестко закодированных значений.

В моем примере не было логического управления потоком (например, вашего примера), но он работал бы так же, если бы это было.

Другим способом сделать это будет создание шаблона типа Service-locator, в котором вы запрашиваете контейнер Dependency Injection, чтобы получить экземпляр класса конфигурации "на лету". Service-Location считается анти-шаблоном в целом, но может по-прежнему вам пригодиться.