考虑以下示例:
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和Verify后,容器无法更改。)
一种解决方案可能是将ISettingsManager
作为依赖项注入CommunicationClient
但我真的不想传递它,因为它会提供超过所需信息。
编辑:容器注册
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);
有关如何以更清洁的方式实现上述目标的任何建议/替代解决方案?感谢。
答案 0 :(得分:6)
Simple Injector在首次使用后锁定容器以进行进一步更改。这是一个明确的设计选择,描述为here。这意味着您在致电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容器为您构建每个实例。让容器自动连线实例可以降低Composition Root的维护负担,并且可以更轻松地将横切关注点(例如使用装饰器)应用于相关类组。对于SettingsManager
和SqlSettingsManager
类,它们的构造函数不太可能经常更改它会增加Composition Root的维护负担。因此,手动创建这些实例一次非常好。
答案 1 :(得分:1)
如果我理解正确,要创建CommunicationClient
课程,您需要传递通过在ISettingsManager
的实例上调用方法检索的信息,但您不想将ISettingsManager
作为依赖关系传递给您的CommunicationClient
?
对此的一个解决方案是创建和注册一个工厂,该工厂将依赖ISettingsManager
并且将具有将返回已配置客户端的CreateClient
方法。
public class CommunicationClientFactory : ICommunicationClientFactory
{
public CommunicationClientFactory(ISettingsManager settingsManager) {...}
public CreateClient() {...}
}
这样,您的CommunicationClient
不依赖于ISettingsManager
,而您只有这家工厂负责创建您的实例。
编辑:
另一种方法是,如果您不想为此创建工厂,则可以在&#34;无效&#34;中创建CommunicationClient
对象。 state,并有一个方法可以设置设置并使其状态有效。
类似的东西:
public class CommunicationClient : IClient
{
public CommunicationClient() { ... }
// Code
CommunicationClient WithSettings(IServerSettings settings) { ... }
}
当然,您必须确保用户在尚未通过设置时不使用它,如果情况确实如此,可能会发送异常。我更喜欢这个解决方案,因为它不太明确,你需要这些设置让你的对象处于正确的状态。