6. Oktober 2009

Scripting Configuration for C#

Configuration is a scripting task.

Since I am fed up with the bracket mess of mono/.NET web apps, I was thinking about a more convenient configuration system. Most configuration systems lack flexibility, because they just evaluate a static file and set some values. Some allow for includes, but not much more. But advanced applications need more flexibility, in other words: code. If the config file is a script and has access to the application, then you have all the flexibility you need, and type safety.

And what is the most natural config script language for a C# project? C#, of course.

All you need is a script engine like CS-Script. Add the script engine, create a script host, load the config script file on application start and execute it. The best part is, that the configuration file is type safe and can be edited by the usual development system.

Here are my code bits:

Add the CSScriptLibrary.dll to the project References.

Create a class that implements the script file loader: ScriptedConfig.cs

using System;

namespace MyApp
{
public class ScriptedConfig : MarshalByRefObject
{
public string BasePath { get; set; }

public void Include(string sFile)
{
using (CSScriptLibrary.AsmHelper helper =
new CSScriptLibrary.AsmHelper(CSScriptLibrary.CSScript.Load(
BasePath + sFile, null, true)))
{
helper.Invoke("*.Configure", this);
}
}
}
}
A class that implements my specific configuration object derived from ScriptedConfig: MyConfig.cs:
using System;
using System.Collections.Generic; // just for the sample Dictionary

namespace MyApp
{
public class MyConfig : ScriptedConfig
{
// Just some sample values
public string sText1_ = "empty1";
public string sText2_ = "empty2";

// A sample dictionary to hold more values
public Dictionary<string, string> values_ = new Dictionary<string, string>();
public Dictionary<string, string> Values { get { return values_; } }

public string Text2 { get { return sText2_; } }
}
}
The application startup code:

MyConfig myconfig = new MyConfig ();
myconfig.BasePath = AppDomain.CurrentDomain.BaseDirectory; // For my web app
myconfig.Include("ConfigGlobal.cs");
I have a global config file, that is the same for all installations and a local config file that overrides the global setting for the test syste, the stage system or the production system. This is the ConfigGlobal.cs:
using System;
using MyApp;

public class Script
{
public static void Configure(MyConfig c)
{
c.sText1_ = "Global Config 1";
c.sText2_ = "Global Config 2";

c.Include("ConfigLocal.cs");
}
}
The global config file invokes the local config file, which configures the same object. ConfigLocal.cs:
using System;
using MyApp;

public class Script
{
public static void Configure(MyConfig c)
{
c.sText2_ = "Local Config 2";
c.values_["Text3"] = "Local Config 3";
}
}
You access the config values like:

string t1 = myconfig.sText1_;
string t2 = myconfig.Text2;
Actually, in my current project I separate my code from the implementation of MyConfig by a facade pattern using an interface (IConfig) and in addition access the config instance via StructureMap and a global static wrapper (Config).

It rather looks like:

string t1 = Config.Get("Text1", "Default1");
string t3 = Config.Instance().Values["Text3"];
But that's for later.

_happy_configuring()

Kommentar veröffentlichen