Как сделать регистрацию в Simple Injector после вызова GetInstance / альтернативного решения?

рассмотрим следующий пример:

public class CommunicationClient : IClient
{
    public CommunicationClient(IServerSettings settings) { ... }
    // Code         
}

public class SettingsManager : ISettingsManager
{
    SettingsManager(IDbSettingManager manager)

    // Code
    public IDictionary<string, string> GetSettings() { ... }
}

: При выполнении регистрации (используя SimpleInjector), мне нужно предоставить значения, полученные из экземпляра SetingsManager и заполнить ServerSettings экземпляр (конкретный тип для IServerSettings), но если я называю GetInstance<ISettingsManager> перед регистрацией CommunicationClient, это дает мне ошибку, что я не могу сделать это
: контейнер нельзя изменить после первого вызова GetInstance, GetAllInstances и проверить.)

одно решение смогло быть впрыснуть ISettingsManager как зависимость CommunicationClient но я действительно не хочу передавать его, поскольку он предоставит ему больше, чем требуется.

EDIT: Регистрация Контейнер

container.Register(typeof(ICommunicationClient), typeof(CommunicationClient));
ISettingsManager settingsManager = container.GetInstance<ISettingsManager>();

string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");

container.Register(typeof(IServerConfiguration), () => 
      new ServerConfiguration(url, userName, password);

любые предложения / альтернативные решения о том, как достичь выше более чистым способом? Спасибо.

2 ответов


простой инжектор запирает контейнер для дальнейших изменений после своей первой пользы. Это явный выбор дизайна, который описывается здесь. Это означает, что вы не можете назвать Register после того, как вы назвали GetInstance, но для этого никогда не должно быть причин. Или, другими словами, ваша конфигурация всегда может быть переписана таким образом, что вам это не нужно. В вашем случае ваша конфигурация, вероятно, будет выглядеть примерно так:

var settingsManager = new SettingsManager(new SqlSettingManager("connStr"));

container.RegisterSingle<ISettingsManager>(settingsManager);
container.Register<ICommunicationClient, CommunicationClient>();

string url = settingsManager.GetSetting("url");
string userName = settingsManager.GetSetting("username");
string password = settingsManager.GetSetting("password");

container.Register<IServerConfiguration>(() => 
      new ServerConfiguration(url, userName, password));

там вы увидите, что SettingsManager не создается контейнер. При использовании контейнера DI не требуется, чтобы контейнер DI создавал каждый экземпляр для вас. Позволять экземплярам автоматическ-провода контейнера для вас сделан для того чтобы понизить тяготу обслуживания вашего Состав Корня и упрощает применение сквозных проблем (например, с использованием декораторов) к группам связанных классов. В случае SettingsManager и SqlSettingsManager классов, очень маловероятно, что их конструктор изменится то часто что оно увеличит тяготу обслуживания вашего корня состава. Поэтому совершенно нормально вручную создавать эти экземпляры один раз.


если я правильно понимаю, создать свой CommunicationClient class, вам нужно передать информацию, которая извлекается путем вызова метода на экземпляре вашего ISettingsManager, но вы не хотите проходить ISettingsManager в зависимости от вашего CommunicationClient?

одним из решений для этого было бы создать и зарегистрировать фабрику, которая будет иметь зависимость от ISettingsManager и это было бы CreateClient метод, который вернет настроенный клиент.

public class CommunicationClientFactory : ICommunicationClientFactory
{
   public CommunicationClientFactory(ISettingsManager settingsManager) {...}

   public CreateClient() {...}
}

таким образом, ваш CommunicationClient не зависит от ISettingsManager и у вас есть только эта фабрика, которая выполняет работу по созданию вашего экземпляра.

изменить: Альтернативой, если вы не хотите создавать фабрику для этого, было бы иметь свой CommunicationClient объект будет создан в "недопустимом" состоянии и иметь метод, который установит параметры и сделает его состояние допустимым.

что-то типа:

public class CommunicationClient : IClient
{
    public CommunicationClient() { ... }
    // Code

    CommunicationClient WithSettings(IServerSettings settings) { ... }
}

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