Как сделать регистрацию в 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) { ... }
}
конечно, тогда вам нужно будет убедиться, что пользователь не использует его, когда настройки еще не были переданы, потенциально отправляя исключение, если это будет так. Мне нравится это решение меньше, потому что менее ясно, что вам нужны эти настройки, чтобы ваш объект находился в правильном состоянии.