Break Away From XML Configurations with ConfigR

Configuration files within ASP.NET applications can often seem a bit convoluted.

Sections strewn all over the place, settings with nothing but strings everywhere and last but certainly not least, it’s all in XML. While this can be just fine for most scenarios, some developers may want or need more out of their configuration files and this is exactly where something like ConfigR comes in.

What is ConfigR?

ConfigR is an open source project by Adam Ralph that allows you to write C# code within your configuration files instead of XML. Nearly any C# code that you want or that you could imagine and the best part about it? It just works.

The project itself is powered by ScriptCS, another powerful tool for writing C# in nearly any environment or editor you desire, and the almighty Roslyn compiler. Both of these are awesome projects of their own and if you aren't familiar with them, I would highly suggest paying a visit to their respective repositories on GitHub.

It Sounds Complicated; It Must Be Hard to Set Up Right?

One would think that allowing C# code to be used as a configuration file would be quite complex to set up (as is the nature of config files), but it really couldn't be further from the truth.

Setting it up as as easy as installing a NuGet package within your application via the Package Manager Console :

PM> Install-Package ConfigR  

or by searching for “ConfigR” within the NuGet Package Manager dialog :

ConfigRDialog

Your application should now have everything it needs to get started using ConfigR. You’ll notice that there are a few different dependency packages that ConfigR relies on that were installed as well, one of them being ScriptCS. This is because ScriptCS is really the package that makes all of this possible (by allowing you to write C# in text files or any other types of files), but you don’t really need to know anything more than that for these purposes.

Getting Started: From .config To .csx

Let’s get started by putting together a quick example to demonstrate what ConfigR can do and how easy it is to use. For demonstration purposes, we will just build a simple Console application, but this could easily be extended to any Web Applications as well.

  • Create a new Console Application within Visual Studio.
  • Add the ConfigR Package through NuGet.

Now you’ll want to create a new class file within your application called “Application.csx”, which will function as your ConfigR configuration file and contain all of your C# code. It’s very important to note that since the compiler is going to need to be able to access this at run-time that you go under properties and set the Copy to Output Directory property to Copy Always :

CopyAlways

Note: How you access your configuration will depend on how your .csx file is named. If you don’t want to explicitly point to the file you are using for a config, name your .csx file so that it is prefixed with the name of your application (e.g. YourConsoleProject.exe.csx).

Now, you’ll want to open up your new .csx file and add a few simple configuration settings to test things out. Since ConfigR supports types other than strings, try adding the following line into the Application.csx (or otherwise named) file :

Add("The Answer to Life, The Universe, and Everything", 42);  

The Add() method is going to be your bread and butter in ConfigR as it actually adds the specific key-value pairs within your configuration. It functions quite similar to how you might expect a Dictionary<string, object> to function.

So now to actually retrieve the value within your application, just use the following section of code that references your key :

// Grab your value from the config
var answer = Config.Global.Get<int>("The Answer to Life, The Universe, and Everything");  

or if you used a name that differs from that of your application (such as Application.csx), you’ll need to wire up the configuration to point at that file :

// Indicate the file you are using as a Config (along
// with any related assemblies)
Config.Global.LoadScriptFile("Application.csx", AppDomain.CurrentDomain.GetAssemblies());  
// Grab your value from the config
var answer = Config.Global.Get<int>("The Answer to Life, The Universe, and Everything");  

If you output this value via the Console, you’ll see that it is working as expected :

What is the answer to the ultimate question? 42  

And that’s really it. In the next section, we will take a look at extending this same type of functionality to support more complex classes.

Now With Web Applications

Just as you might expect, ConfigR is just as easy to set up for Web Applications as it is for Console ones. In this example, we will look into using custom classes within our configuration.

To get started, we will be doing quite a bit of what you saw in the previous section for Console applications :

  • Create a new, empty ASP.NET Application.
  • Add any references you would like (Web Forms, MVC or Web API).
  • Add the ConfigR Package through NuGet.
  • Create a new .csx Config File within your application (and don’t forget to set it to Copy Always).

Next, we will create a new class within your application that will have a bit more logic than just a simple primitive type. Create a new class within your application called Widget.cs that looks like the following :

namespace ConfigRExamples.Models  
{
    public class Widget
    {
         public Guid SerialNumber { get; set; }
         public string Description { get; set; }
         public DateTime ManufacturedDate { get; set; }
         public IEnumerable<WidgetPart> Parts { get; set; }
    }
    public class WidgetPart
    {
         public int PartNumber { get; set; }
         public string Description { get; set; }
         public bool IsFunctioning { get; set; }  

         public WidgetPart(int partNumber, string description, bool isFunctioning = true)
         {
              PartNumber = partNumber;
              Description = description;
              IsFunctioning = isFunctioning;
         }
    }
}

Now, we will define a build a few sample widgets within our configuration file that we will be able to access within our application. Open your configuration file and add the following bits in :

using ConfigRExamples.Models;

// Build a collection of widgets, which we will store in
// the application
var widgets = new List();

// Build a few widgets
widgets.Add(new Widget(){  
                SerialNumber = Guid.NewGuid(),
                Description = "Widget A",
                Parts = new List<WidgetPart>() { 
                      new WidgetPart(1, "Motorized Arm"), 
                      new WidgetPart(2, "Voice Module", false) 
                }
        });
widgets.Add(new Widget(){  
                SerialNumber = Guid.NewGuid(),
                Description = "Widget B",
                Parts = new List<WidgetPart>() { 
                      new WidgetPart(4, "Kung Fu Grip") }
        });
widgets.Add(new Widget(){  
                SerialNumber = Guid.NewGuid(),
                Description = "Widget C",
                Parts = new List<WidgetPart>() { }
        });;

// Add these widgets to the config
Add("Widgets", widgets);  

Now if you were using this within an MVC application, you would want to wire up your configuration (if you were using a differently named file) within the Global.asax.cs file :

public class MvcApplication : System.Web.HttpApplication  
{
        protected void Application_Start()
        {
            // Load up your configuration
            Config.Global.LoadScriptFile("Widgets.csx", AppDomain.CurrentDomain.GetAssemblies());
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
}

Now if you built a WidgetsController, you could grab your widgets in all of their strongly-typed glory, and pass them to a View to easily see all of their properties :

public class WidgetsController : Controller  
{
        public ActionResult Index()
        {
            // Grab your widgets from the Configuration
            var widgets = Config.Global.Get<IEnumerable>();

            // Pass the widgets to the View
            return View(widgets);
        }
}

and style your View to look like the following :

@model IEnumerable<ConfigRExamples.Models.Widget>
<!DOCTYPE html>  
<html>  
<head>  
    <meta name="viewport" content="width=device-width" />
    <title>Widgets</title>
</head>  
<body>  
    <!-- Loop through each Widget -->
    @foreach(var widget in Model)
    {
        <h4>@widget.Description (@widget.SerialNumber)</h4>

        if(widget.Parts.Any())
        {
             <b>Parts</b>
             <ul>
             @foreach (var part in widget.Parts)
             {
                   <li>@part.Description (@(part.IsFunctioning ? "Working" : "Requires Repairs"))</li>
             }
             </ul>
       }
       else
       {
            <b>No Parts Available</b>
       }
   }
</body>  
</html>  

After building your application, you should the be able to run it and see that all of these properties are loaded as expected via ConfigR!

ConfigRResults

What Else Can It Do?

The examples above were extremely basic use cases for ConfigR and how it could be used as a simple data store, but let’s say you wanted to do something a bit more dynamic.

ConfigR isn’t limited to the confines of your application either. Since you can write just about any C# code within your .csx file, if you wanted to pull data based on your current environment from a database, it’s as easy as opening up a connection :

// Add the appropriate references to open a SQL Connection
using System.Data.SqlClient;

// Pull some data from the database to use as a setting 
using(var connection = new SqlConnection("Your Connection String"))  
{
     // Build a query to pull some data
     var query = "SELECT * FROM YourTable WHERE Application = @Application";

     // Build a command to execute the query
     using(var command = new SqlCommand(query, connection))
     {
            // Open your connection
            connection.Open();

            // Add your parameter (perhaps based on a setting or 
           // some environmental variable(s))
            command.Parameters.AddWithValue("@Application", Environment.Something)

            // Store the results in the configuration
            using(var reader = command.ExecuteReader())
            {
                // Add your properties as you iterate through the 
                // results here
                Add("Property", reader["Property"]);
            }
     }
}

This functionality could introduce quite a bit of flexibility if you needed to configure different settings for different users, environments, versions or just about anything else you could think of.

Another great feature of ConfigR is that is actually allows you to load configuration settings directly from an HTTP endpoint instead of just local files :

// Target your remote file (in this case a file hosted as a Gist on GitHub)
Config.Global.LoadWebScript(new Uri("https://gist.githubusercontent.com/Rionmonster/21e70f6bbb93638a694a/raw/041b0f0c88b6e5f881a5fd1ee5b1af97ac021b48/RemoteConfig.csx"), AppDomain.CurrentDomain.GetAssemblies());  

You could conceivably couple these same concepts and store different configuration data or files for within a database and dynamically target one or another based on what you need to accomplish. This would essentially allow you to completely decouple your configurations from your application.

The sky is really the limit.

Join In The Fun

As mentioned earlier, ConfigR is completely open source so if it is something that interests you or that you would be interested in working on, visit the ConfigR repository on GitHub. The repository itself has some excellent information including examples and the issues area contains quite a few good in-depth discussions if you run into issues as well.

At the time of writing, the project was currently at v0.12, so still quite early in its development, but it’s certainly something that I know that I’ll be keeping my eye on and using quite a bit going forward.

You can access a few of the examples that were discussed within this post on GitHub using the link below :

comments powered by Disqus