How to do a registration in Simple Injector after a GetInstance call / Alternate solution?

帅比萌擦擦* 提交于 2019-12-05 07:26:20

Simple Injector locks the container for further changes after its first use. This is an explicit design choice, which is described here. This means that you can't call Register after you called GetInstance, but there should never be a reason to do this. Or in other words, your configuration can always be rewritten in a way that you don't need this. In your case your configuration will probably look something like this:

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));

There you see that SettingsManager is not built-up by the container. When using a DI container, you are not required to let the DI container build up every instance for you. Letting the container auto-wire instances for you is done to lower the maintenance burden of your Composition Root and makes it easier to apply cross-cutting concerns (using decorators for instance) to groups of related classes. In the case of the SettingsManager and SqlSettingsManager classes, it is very unlikely that their constructor will change that often that it will increase the maintenance burden of your Composition Root. It's therefore perfectly fine to manually create those instances once.

If I understand correctly, to create your CommunicationClient class, you need to pass information that are retrieved by calling a method on an instance of your ISettingsManager, but you don't want to pass the ISettingsManager as a dependency to your CommunicationClient?

One solution for that would be to create, and register, a factory that would have a dependency on ISettingsManager and that would have a CreateClient method that would return the configured client.

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

   public CreateClient() {...}
}

This way your CommunicationClient is not dependent on the ISettingsManager and you have just this factory that does the work of creating your instance.

Edit: An alternative, if you don't want to create a factory for this, would be to have your CommunicationClient object be created in an "invalid" state, and have a method that would set the settings and make its state valid.

Something like:

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

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

Of course, then you'd have to make sure that the user don't use it when the settings have not been passed yet, potentially sending an exception if that would be the case. I like this solution less, because it's less explicit that you NEED those settings to have your object in a correct state.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!