DI-Container: how to pass configuration to objects

Sometimes I have classes that need to get some information to build. I'm not talking about references to other objects (which will be entered), but about (for example) strings that contain unique information:

// Scoped as singleton! class Repository { public Repository( InjectedObject injectedObject, string path ) { ... } } 

How did you enter this line? One possibility is to write the Init() method and avoid the injection for the string:

 class Repository { public Repository( InjectedObject injectedObject ) { ... } public void Init( string path ) { ... } } 

Another possibility is to wrap information in an object that you can enter:

 class InjectedRepositoryPath { public InjectedRepositoryPath( string path ) { ... } public string Path { get; private set; } } class Repository { public Repository( InjectedObject injectedObject, InjectedRepositoryPath path ) { ... } } 

Thus, I need to create an instance of InjectedRepositoryPath during the initialization of my DI container and register this instance. But I need such a unique configuration object for each similar class.

Of course, I can enable RepositryFactory instead of the Repository object, so the factory will ask me for the path:

 class RepositoryFactory { Repository Create( string path ) { ... } } 

But then again, this is one factory only for a singleton object ...
Or finally, since the path will be extracted from the configuration file, I could skip the path around the line and read the configuration in my constructor (which is probably not so optimal, but possible):

 class Repository { public Repository( InjectedObject injectedObject ) { // Read the path from app config } } 

What is your favorite method? For non-Singleton classes, you should use imho to solve Init() or factory, but what about objects with a single scope?

+4
source share
3 answers

I prefer not to have a DI container to dictate my design API. The container should fit the right design, not the other way around.

Create your classes in DI-friendly , but don't go along with your DI container. If you need a connection string, then take the line through the constructor:

 public class Repository : IRepository { public Repository(string path) { //... } } 

Many DI containers can deal with primitive values. For example, here is one way to do this using Windsor:

 container.Register(Component.For<IRepository>() .ImplementedBy<Repository>() .DependsOn( new { path = "myPath" } )); 

However, if your container, by choice, cannot deal with primitive parameters, you can always decorate the Repository implementation that knows how to find the line:

 public class ConfiguredRepository : IRepository { private readonly Repository decoratedRepository; public ConfiguredRepository() { string path = // get the path from config, or whereever appropriate this.decoratedRepository = new Repository(path); } // Implement the rest of IRepository by // delegating to this.decoratedRepository } 

Now you can simply tell your container to map the IRepository to ConfiguredRepository , while keeping the repository core clean.

+2
source

If you use constructor injection, I believe that the best option is to add a parameter that is your configuration object to the constructor. Using the init function, you will bypass the constructor injection point a bit. This makes testing difficult, and also makes maintenance and delivery difficult.

Discovery becomes a problem because it is easy to understand that a configuration object is required for this class. By adding it to the constructor, anyone who uses this object clearly knows that this configuration should be there.

+3
source

Some time ago, Joshua Flanagan showed how custom configuration sections can be treated as dependencies and provided using the constructor insert.

http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/07/12/how-we-handle-application-configuration.aspx

+1
source

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


All Articles