Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to, using dependency injection, get configuration from multiple sources?

I'm using Simple Injector, but maybe what I need is more of a conceptual answer.

Here's the deal, suppose I have an interface with my application settings:

public interface IApplicationSettings
{
    bool EnableLogging { get; }
    bool CopyLocal { get; }
    string ServerName { get; }
}

Then, one would usually have a class which implements IApplicationSettings, getting each field from a specified source, for instance:

public class AppConfigSettings : IApplicationSettings
{
    private bool? enableLogging;
    public bool EnableLogging
    {
        get
        {
            if (enableLogging == null)
            {
                enableLogging = Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"];
            }
            return enableLogging;
        }
    }
    ...
}

HOWEVER! Let's say I want to get EnableLogging from app.config, CopyLocal from database, and ServerName from another implementation which gets the current computer name. I want to be able to mix-match my app configuration without having to create 9 implementations, one for each combination.

I'm assuming that I can't pass any parameters because the interfaces are resolved by the injector (container).

I thought of this, initially:

public interface IApplicationSettings<TEnableLogging,TCopyLocal,TServerName>
where TEnableLogging : IGetValue<bool>
where TCopyLocal : IGetValue<bool>
where TServerName : IGetValue<string>
{
    TEnableLogging EnableLog{get;}
    TCopyLocal CopyLocal{get;}
    TServerName ServerName{get;}
}

public class ApplicationSettings<TEnableLogging,TCopyLocal,TServerName>
{
    private bool? enableLogging;
    public bool EnableLogging
    {
        get
        {
            if (enableLogging == null)
            {
                enableLogging = Container.GetInstance<TEnableLogging>().Value
            }
            return enableLogging;
        }
    }
}

However, with this I have one main problem: How do I know how to create an instance of TEnableLogging (which is a IGetValue<bool>)? Oh, assume that IGetValue<bool> is an interface which has a Value property, which will be implemented by the concrete class. But the concrete class may need some specifics (like what's the name of the key in app.config) or not (I may simply want to return always true).

I'm relatively new to dependency injection, so maybe I'm thinking in a wrong way. Does anyone have any ideas on how to accomplish this?

(You may answer using another DI library, I won't mind. I think I just need to grab the concept of it.)

like image 381
Conrad Clark Avatar asked Mar 11 '13 18:03

Conrad Clark


People also ask

Which configuration can be used for dependency injection?

The di. xml file configures which dependencies are injected by the object manager. You can also specify sensitive configuration settings using di. xml .

What is the difference between dependency inversion and dependency injection?

The Inversion of Control is a fundamental principle used by frameworks to invert the responsibilities of flow control in an application, while Dependency Injection is the pattern used to provide dependencies to an application's class.

What is dependency injection stack overflow?

Dependency injection is a pattern to allow your application to inject objects on the fly to classes that need them, without forcing those classes to be responsible for those objects. It allows your code to be more loosely coupled, and Entity Framework Core plugs in to this same system of services.

How do we implement dependency injection?

Dependency Injection is done by supplying the DEPENDENCY through the class's constructor when creating the instance of that class. The injected component can be used anywhere within the class. Recommended to use when the injected dependency, you are using across the class methods.


1 Answers

You are definitely heading the wrong way here.

Some years ago I built an application that contained an interface much like your IApplicationSettings. I believe I named it IApplicationConfiguration, but it contained all application's configuration values as well.

Although it helped me make my application testable at first, after some time the design started to get in the way. A lot of implementations depended on that interface, but it kept changing a lot and with it the implementation, and the test version.

Just like you I implemented some lazy loading, but this had a terrible down side. When one of the configuration values was missing, I only found out that it did when the value was called for the first time. This resulted in a configuration that was hard to verify.

It took me a couple of iterations of refactoring to find out what the core of the problem was. Big interfaces are a problem. My IApplicationConfiguration class was violating the Interface Segregation Principle and the result was poor maintainability.

In the end I found out that this interface was completely useless. Besides violating the ISP, those configuration values described an implementation detail and instead of making an application wide abstraction, it is much better to supply each implementation directly with the configuration value they need, and only the values they need.

When you do this, the easiest thing to do is to wrap those values into a Parameter Object (even if it is just one value), and inject those configuration values into the constructor. Here's an ecample:

var enableLogging =
    Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"]);

container.RegisterSingleton(new LoggerSettings(loggingEnabled: enableLogging));

In this case, LoggerSettings is a configuration object specific to Logger, which requires it as constructor argument.

When doing this, the enableLogging value is read just once from the configuration file and is done so during application startup. This makes it fast and makes it fail at application startup when the value is missing.

like image 63
Steven Avatar answered Oct 04 '22 01:10

Steven