Wednesday, December 3, 2008

Data as code: Storing Configuration in C# & Asp.net

I've been thinking about the best way to store configuration information in c# since I started this blog more than a year ago. I'm a little embarrased that it's taken me so long, but I think I've hit on the best way of doing it (largely inspired by Ayende's JFHCI). Here it is:

Just use c#

That's right! No xml. No web.config. No database. Just use c#.

The benefits are pretty obvious. The reason configuration is a pain is because you have to spend several lines of code extracting information from some foreign (and usually weakly-typed) source, and loading it into c#. You have to worry about checking for null "just in case", etc. Keeping everything in c# saves a lot of code and is radically simpler. This approach is popular in scripting languages like Ruby.

It's less obvious why the apparent drawbacks aren't actually drawbacks. Some typical objections:

  1. But then the configuration is compiled-in. You can't modify it on the fly!
    This is false in Asp.net. Although there is a pre-compilation option, ever since 2.0 the default has been to read the source files and compile them into temporary assemblies on the fly. This would be a valid criticism for a WinForms app. But seriously, who uses WinForms?
  2. But that's just hard-coding!
    Yes, it is! Embrace it.
  3. Storing configuration data in c# is ugly and unintuitive because c# isn't a good data format.
    True, c# is not lisp or json or xml. Initializing a complex data type the traditional way would involve declaring the type, then initializing it imperatively in a constructor. This separation means that the code does not look like data in the way that traditional config files do.

    But with c# 3.0, I think the language is good enough at representing data to overcome this objection. Collection initializers and object initializers are expressive enough that you should never need a constructor to do initialization.
Let's run with this a little bit. Say you need to add a list of email addresses for generating error emails. Just pop open config.cs, and type this:

public static var ErrorEmails = new List<string>() {
    "joe@bob.com",
    "jane@bob.com",
    "julie@bob.com"
}

That's not so bad, is it? Clear, strongly typed, native c#, at least as easy to modify on-the-fly as xml or database configuration. Let's take a more complicated example: Suppose our application allows the user to submit different types of Requests, and each Request has a bunch of metadata associated with it: a code, request group, name, and description. Also suppose we want to look up the Request metadata given a code. We can do something like this:

public static var Codes = new Dictionary<string, CodeData>() {
    { "Code1", new CodeData { Group="...", Name="...", Desc="..." } },
    { "Code2", new CodeData { Group="...", Name="...", Desc="..." } },
    { "Code3", new CodeData { Group="...", Name="...", Desc="..." } },
    { "Code4", new CodeData { Group="...", Name="...", Desc="..." } }
}
That is the sort of information that I probably would have placed in a database before. The benefits of doing it this way should be obvious. Instead of dropping down into SQL to query the data and loading it into c#, I can now start off in c#. I can use LINQ to easily query the data to return anything I need.

Yes, it's unconventional. Do it anyway.

No comments: