How to register with Simple Injector after calling GetInstance / Alternative solution?

Consider the following example:

public class CommunicationClient : IClient { public CommunicationClient(IServerSettings settings) { ... } // Code } public class SettingsManager : ISettingsManager { SettingsManager(IDbSettingManager manager) // Code public IDictionary<string, string> GetSettings() { ... } } 

Problem : When performing registration (using SimpleInjector ), I need to provide the values ​​obtained from the SetingsManager instance and populate the ServerSettings instance (specific type for IServerSettings ), but if I call GetInstance<ISettingsManager> before registering CommunicationClient >, it gives me an error that I am not I can do this Error : The container cannot be changed after the first call to GetInstance, GetAllInstances and Verify.)

One solution might be to make ISettingsManager dependent on CommunicationClient , but I really don't want to pass it on since it would provide it with more than necessary information.

EDIT: Container Registration

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

Any suggestions / alternative solutions on how to achieve higher in purer form? Thanks.

+6
source share
2 answers

A simple injector locks the container for further changes after its first use. This is the explicit design choice that is described here . This means that you cannot call Register after calling GetInstance , but there should never be a reason for this. Or, in other words, your configuration can always be rewritten in such a way that you do not need it. 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)); 

Here you see that the SettingsManager not created by the container. When using a DI container, you are not required to let the DI container grow each instance for you. Providing containers with automatic checks for you has been done to reduce the burden of servicing your “Composition Root” and simplifies the application of cross-cutting problems (using, for example, decorators) for groups of related classes. In the case of the SettingsManager and SqlSettingsManager classes, their constructor is unlikely to change so often that it will increase the load on maintaining your composition root. Therefore, it is perfectly fine to manually create these instances once.

+5
source

If I understood correctly, in order to create your CommunicationClient class, you need to pass the information received by the method call in the instance of your ISettingsManager , but you do not want to pass the ISettingsManager as a dependency on your CommunicationClient ?

One solution to this would be to create and register a factory, which will be dependent on ISettingsManager , and it will have a CreateClient method that will return the configured client.

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

Thus, your CommunicationClient is independent of ISettingsManager , and you only have this factory that does the work of creating your instance.

Edit: An alternative, if you do not want to create a factory for this, would be to create your CommunicationClient object in an "invalid" state and have a method that sets the settings and makes its state valid.

Sort of:

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

Of course, then you will need to make sure that the user is not using it when the settings have not yet been passed, perhaps throwing an exception if that is the case. I like this solution less because it is less explicit that you need these settings to display your object in the correct state.

+1
source

Source: https://habr.com/ru/post/979093/


All Articles