C# 在GetInstance调用/替代解决方案之后,如何在Simple Injector中进行注册?

C# 在GetInstance调用/替代解决方案之后,如何在Simple Injector中进行注册?,c#,ioc-container,simple-injector,C#,Ioc Container,Simple Injector,考虑以下示例: public class CommunicationClient : IClient { public CommunicationClient(IServerSettings settings) { ... } // Code } public class SettingsManager : ISettingsManager { SettingsManager(IDbSettingManager manager) // Cod

考虑以下示例:

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

public class SettingsManager : ISettingsManager
{
    SettingsManager(IDbSettingManager manager)

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

关于如何以更清洁的方式实现上述目标,有何建议/替代解决方案?谢谢。

如果我理解正确,要创建
CommunicationClient
类,您需要传递通过调用
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) { ... }
}

当然,您必须确保用户在设置尚未通过时不使用它,如果是这种情况,可能会发送异常。我不太喜欢这个解决方案,因为它不太明确,您需要这些设置才能使对象处于正确的状态。

Simple Injector在首次使用后锁定容器以进行进一步更改。这是一个明确的设计选择,如下所述。这意味着您不能在调用
GetInstance
后调用
Register
,但不应该有这样做的理由。或者换句话说,您的配置总是可以以您不需要的方式重写。在您的情况下,您的配置可能如下所示:

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));
var-settingsManager=new-setingsmanager(new-SqlSettingManager(“connStr”);
容器.RegisterSingle(设置管理器);
container.Register();
字符串url=settingsManager.GetSetting(“url”);
字符串userName=settingsManager.GetSetting(“用户名”);
字符串password=settingsManager.GetSetting(“密码”);
容器。寄存器(()=>
新服务器配置(url、用户名、密码);

在这里,您可以看到
SettingsManager
不是由容器构建的。在使用DI容器时,不需要让DI容器为您构建每个实例。让容器为您自动连接实例是为了降低您的维护负担,并使横切关注点(例如使用装饰器)更容易应用于相关类组。在
SettingsManager
sqlsetingsmanager
类的情况下,它们的构造函数不太可能经常更改,从而增加组合根的维护负担。因此,手动创建这些实例一次就可以了。

这可以解决问题,但工厂在这里不是一种过火的行为吗?另外,我正在使用IoC容器(SimpleInjector)来获取实例并管理它们的生命周期。然后我必须将IoC容器传递给CommunicationClientFactory来创建实例。我不认为将IoC容器传递给工厂是一个大问题,因为它只是一个工厂,您还可以在其中包含更多逻辑。对于SimpleInjector,我认为没有其他选择,因为在使用匿名工厂(注册
Fun
)时,无法在构造函数中传递值。我同意将容器注入工厂不是问题,只要工厂实现是在合成根中定义的。然而,使用Simple Injector,您可以注册一个匿名工厂(使用Func),没有任何问题,没有任何限制可以阻止您这样做。不过,在这种情况下,我确实将工厂的使用视为无用的额外间接层。@Steven哇,谢谢,我觉得自己真的很笨,但我从来没有想到我可以调用
Container.getInstance()
到我的lambda表达式中,所以我总是要传递在容器中配置的参数。。。Woohoo重构前进:)我已经编辑了这个问题,包括容器注册。谢谢。@Steven我正在使用大量分散在代码中的AutoMapper配置文件,它们在合成根目录中注册,可能依赖于一些类似的服务。我是否应该手动创建所有必需的服务?我需要能够轻松地更改设置管理器。@mrmashal请在SO或Github上发布一个新问题,并提供一些代码来描述您的用例。没有这样的背景,很难给出具体的反馈。
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));