C# OOP – How/When to Build a Singleton That Stores Configuration Data

cobject-oriented-design

I am a junior developer (think "intern-level" experience) working at a small shop more or less alone on a project that consumes data from a database, processes it, and inserts the results into another database. The solution is a Windows service written in C#.

A previous version of the solution used App.config for things like connection strings, time intervals for checking the db for new data, etc. The project manager wants for this information to be stored in a table in the db, and then have a singleton keep that data rather than needing to access the config file. The configuration data needs to be loaded at startup.

My current implementation has the singleton's constructor make a single call to getConfig(), a method that encapsulates the work of getting all the data from the db. We are now in the process of writing code that will check the db at startup to make sure those configuration tables are there and will build them if they aren't.

I have several uncertainties about the design:

1) Is building the config in the constructor the best place so the work is implicit:

protected override void OnStart(string[] args)
{
    // The first call for an instance, so the object is created & data is loaded
    ConfigSingleton MyConfig = ConfigSingleton.GetInstance;

    // Create object that checks db for data to process; relies on config
    Poller = new Poller(); 
    Poller.Start();
}

or should I make an explicit public call to getConfig() during startup for the sake of clarity:

protected override void OnStart(string[] args)
{
    ConfigSingleton MyConfig = ConfigSingleton.GetInstance;
    MyConfig.getConfig(); // Explicit call to get config from db

    // Create object that checks db for data to process; relies on config
    Poller = new Poller(); 
    Poller.Start();
}

2) Should the work to check for the config tables be done by a separate, presumably static, class:

protected override void OnStart(string[] args)
{
    if (!DBChecker.isTableReady())
    {
        DBChecker.BuildTables();
    }

    ConfigSingleton MyConfig = ConfigSingleton.GetInstance;
    MyConfig.getConfig();

    // Create object that checks db for data to process; relies on config
    Poller = new Poller(); 
    Poller.Start();
}

or should it be handled internally by the configuration singleton, so the code would look like one of the variations above?

A somewhat unrelated example that helps clarify the spirit of my question comes from a previous version of the solution (which I've paraphrased):

// Inside this function, a call is made to BusinessLayer.GetList1()
MyController = new Controller(FirstParam); 
MyController.Start();

// Yet here, a similar version is called first and then the result is passed as a parameter
List<Objects> MyList2 = BusinessLayer.GetList2();
MySender = new Sender(SecondParam, MyList2);
MySender.Start();

Are there basic rules of thumb that govern when work should be done inside some other work, or outside, passed as parameter, etc, other than the obvious fact that it should at least be consistent?

Best Answer

I actually just solved this issue. I wanted to store configuration data in the database while having the program consume the data. I created a configuration retrieval class (non singleton) that when ever a part of the program needed a configuration value they instantiated the class and called the GetConfigValue(ConfigPropertyEnum item) method. Behind the class was a cache that was only accessed from the configuration retrieval class and a Repository class for interfacing with the database (using Entity Framework).

When I first implemented the configuration retrieval class it was just accessing the database through the repository any time a config value was needed so that I could start writing the rest of the program. After I had validated that the general design would work I implemented the cache with a way to time out cache values so that periodically I would reload an item from the database in case things changed.

This structure got me a loosely coupled configuration. Since the rest of the program does not care how the configuration retrieval class got the value, I can swap out the actual data store from config file to database and back with out any code changes except in the configuration retrieval class. Also since I have an interface I can swap out my cache implementation with out having to change the rest of the program as long as the interface remains the same.

Overall it has provided a robust and extendable config class. When I want to add a new configuration variable I can add to the enum and table and have the value available any where in the program.

As to how to get configuration values to other parts of your program? I tend to like dependency injection with my classes taking an instance of IConfiguration so I can use different implementations if I am testing.

Related Topic