Ioan Lazarciuc's Weblog I program, therefore I exist.

20Jan/081

Managing "Global Constants" with a Database

Introduction

There always comes a time while developing an application when a programmer has to introduce some "constants". These may be Id's, names, time intervals. The first action is to just put the literal constant where it's needed first.

Once the value of the constant is needed in additional places, a decision has to made: where to store the "constant". The really bad answer would be to just reuse the literal constant. A better approach would be to create a "const" or "readonly" field with the required value. This is perfectly fine if the constant has a very little chance of changing it's value (due to specifications change some of the constants may change). Changes to constants defined in this way can only be performed by recompiling the code and redistributing the new build.

Sometimes the probability of change for a "constant" is so high, that it actually becomes a "global variable" (even though it may continue to be called a "constant"). Recompiling the whole solution now becomes unfeasible.


In the following, I will propose a pattern to apply to some of these "unstable" "global constants". I am referring to the constants that have a high probability of change and change only though one mechanism: by changing the requirements (for example, the maximum time interval during which a response for a client request must be sent). In other words, the application only reads the value of the constant.

The basic idea is to have a static class that holds all such "constants" either as fields or as properties. The values for the fields or properties should be loaded from a "source" before any of the constants are used. This source can be a file, a database, a web service, etc. The advantages of choosing a remote source for the values of the constants is that the application does not have to be versioned (have it's version number increased and redistributed) just because a few constant values have changed. I will demonstrate the use of a SQL Server database table as a source for values.

The code for this article is written in C#, but it can easily be adapted to any .NET language. The required tools are Microsoft Visual Studio 2005 and SQL Server. "Express" versions of the two tools are the minimum required. The source code for the article can be found here. The solution was written in Visual Studio 2008. If you wish to use Visual Studio 2005, you can open the project file (.csproj) instead of the solution file (.sln).

The testing environment is simple: just a console application that prints the values of the defined constants. The constants are held in the GlobalConstants class.

static void Main(string[] args)
{
    Console.WriteLine("String constant value is {0}", GlobalConstants.StringProperty);
    Console.WriteLine("Numerical constant value is {0}", GlobalConstants.NumericalProperty);
    Console.WriteLine("DateTime constant value is {0}",GlobalConstants.DateTimeField);
}

Database Setup

The SQL Server Express database used is named ConstDatabase and is included with the project. The constant values are stored in the table ConstantTable. Each property has a Name(string), a Value(string) and a GUID (global unique identifier). The values are stored as strings and later converted into the required formats in the application. The GUIDs are used in order to refer to a constant in a "name independent" way. Also, the GUIDs avoid collisions with other identically named constants that may appear (when using several constant classes).

NOTE: You may need to change the connection string for the database from the one found in the source code archive.

Application Constant Initialization

We make use of a static constructor in order to ensure that the values of the constants are retrieved as soon as the class is loaded. The main idea is to set the value of the field(property) to the one found in the ConstantTable in the row that has a matching GUID. For this we need to map each field(property) to a GUID. A good way to do this is to create and use a custom attribute. The declarations of the constants would look like this:

private static string _stringProperty = "Default Value";
 
[DatabaseConstant("bfd5bf83-ed8a-4bcc-bbca-c97bf1b89182")]
public static string StringProperty
{
    get { return _stringProperty; }
    set { _stringProperty = value; }
}
 
private static decimal _numericalProperty = -1.0M;
 
[DatabaseConstant("e3a68ed7-5a7a-4987-b53e-bc61d04efa96")]
public static decimal NumericalProperty
{
    get { return _numericalProperty; }
    set { _numericalProperty = value; }
}
 
[DatabaseConstant("821b8fcb-d052-4039-967f-aa89229dcb95")]
public static DateTime DateTimeField = new DateTime();

The DatabaseConstant attribute receives a GUID either as a string or a Guid .NET structure. The point is to have the static constructor "look" at each field and property that the class defines and, for the ones that have the DatabaseConstant attribute applied, get the value from the database, convert it to the proper .NET type and store it in the field (property).

The code for the custom attribute can be found in the archive containing the whole source code for this article.

Note that, even though I mentioned that the values do not change in the application code, we need setters for the properties in order to be able to set the values from the database.

The static constructor uses reflection in order to get all the fields and properties that have the DatabaseConstant attribute applied and to set the values. The ChangeType method of the Convert class is used to convert the string value returned by the database to the type of the property, without specifying each conversion using a switch depending on the data type of the field (property).

static GlobalConstants()
{
    StringBuilder errors = new StringBuilder();
    try
    {
        FieldInfo[] fiarr = typeof(GlobalConstants).GetFields();
        foreach (FieldInfo fi in fiarr)
        {
            try
            {
                // Iterate through all the Attributes for each field.
                foreach (Attribute attr in
                    Attribute.GetCustomAttributes(fi))
                {
                    // Check for the DatabaseConstant attribute.
                    DatabaseConstantAttribute atr = attr as DatabaseConstantAttribute;
                    if (atr != null)
                    {
                        string cVal = GetDatabaseConstantValue(atr.GUID);
                        fi.SetValue(null, Convert.ChangeType(cVal, fi.FieldType));
                    }
                }
            }
            catch (Exception)
            {
                // fallback action
                errors.AppendFormat("Could not initialize constant {0} from the database. Fallback used.\n",
                                    fi.Name);
            }
 
        }
    }
    catch (Exception)
    {
        // fallback action
        Console.WriteLine("Could not initialize constants from database. Fallback values used.");
    }
    if (errors.Length > 0)
    {
        // log the errors
        Console.Write(errors);
    }
}

 

Reflection significantly reduces the amount of code needed to initialize every single field/property. The code for initializing the properties is the same as for the fields, except that we call GetProperties() and use PropertyInfo instead of FieldInfo. The GetDatabaseConstantValue is a simple method that uses a query to retrieve the value for the constant based on the GUID.

Error Handling

The advantages of remotely initializing constants have been covered. But there is also a serious drawback: what happens if for some reason the initialization fails? The database could be offline, the string stored values can be invalid for conversion (eg. numbers). As much as possible, we should have a backup set of values that could provide minimum reliability to the application (instead of just crashing, or reporting that the application cannot continue to run). Also, a logging feature would also be welcomed.

To do all these, try catch blocks are added around the initialization for each field/property, and for each call to the database. If the database call throws an exception, then there might be some connectivity issues. Otherwise, most likely a conversion error occurred. these errors are accumulated in the errors StringBuilder.

After the constructor finishes, if there are errors, they can be logged, or, in this case, displayed in the Console. It is a good practice to make sure that no exceptions will get thrown by the static constructor.

Conclusion

The pattern presented above provides a quick and convenient way to initialize global constants for an application. I am perfectly aware that there are several discussions to be had regarding constant initializations (error handling, complex constants, etc.). I would welcome these discussions, as well as alternative proposals in the comments section of this post.

Comments (1) Trackbacks (0)
  1. This really is a simple, yet ellegant approach to the whole constants-handling issue.

    I like it and I’ll definetly use it in future projects.

    Thanks for the ideas and keep up the good work.

    Best wishes,
    Adi


Leave a comment

No trackbacks yet.