Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Customising the default AppSettings and ConnectionStrings Expression Builders

0.00/5 (No votes)
25 Mar 2009 1  
Use of Expression Builders in different landscapes (Dev / QA / Prod).

Introduction

I've been using CodeProject for a number of years, and I've been meaning to try to contribute back. This is the first go, and I hope someone will find it useful, even if it is not the most elegant.

The idea is to enable all configuration data for multiple landscapes (Dev / QA / Prod etc.) to be available in a single web.config file.

Background

When I have developed Web applications in the past, I have always had a difficulty with having different sets of settings for my local machine, the shared development machine, and the shared production machines. They all would need a slightly different configuration, particularly connection strings to different databases. I managed to overcome it in ASP.NET 1.1 with my own set of classes which could determine which group of settings to use.

With ASP.NET 2.0, I ran back into this limitation as I wasn't able to do this any more and still had to use the Expressions used, say, for the SQLDataSource or the ObjectDataSource. Finally, with some time on my hands and having liked an idea from an article posted on the use of Expression Builders, I set out to produce something which might do the job for me. Note that I haven't attempted to see whether this would work in ASP.NET 3.0 or 3.5.

The idea was to replace the default Expression Builders, meaning that my new DLL would plug in within a few minutes and not require any changes other than to the web.config.

Using the code

To use the DLL, add a reference to it and then add the following in the web.config:

<compilation debug="true">
  <expressionBuilders>
    <remove expressionPrefix="AppSettings"/>
    <remove expressionPrefix="ConnectionStrings"/>

    <add expressionPrefix="AppSettings" 
      type="Cottle.IT.ExpressionBuilders.MyAppSettingsExpressionBuilder, 
            Cottle.IT.ExpressionBuilders"/>
    <add expressionPrefix="ConnectionStrings" 
      type="Cottle.IT.ExpressionBuilders.MyConnectionStringsExpressionBuilder, 
            Cottle.IT.ExpressionBuilders"/>
  </expressionBuilders>
</compilation>

The default Expression Builders are removed and replaced with our Expression Builders.

Both Expression Builders need some help to determine which landscape they are running in. To do this, they look at the SERVER_NAME server variable and compare it against a group of AppSettings variables which have been given a specific set of names:

<appSettings>
  <add key="ServerRegion{Prod}" value="localhost"/>
  <add key="ServerRegion{Dev}" value="localhostd"/>

Then, further settings, either appSettings or connectionStrings, are set up in a similar way:

  <add key="{Prod}Setting1" value="Setting 1 on Prod"/>
  <add key="{Prod}Setting2" value="Setting 2 on Prod"/>
  <add key="{Dev}Setting1" value="Setting 1 on Dev"/>
  <add key="{Dev}Setting2" value="Setting 2 on Dev"/>
  <add key="abc" value="Can be any"/>
</appSettings>
<connectionStrings>
  <add name="{Dev}NorthwindConnectionString"
       connectionString="Data Source=DevServer;Initial Catalog=Northwind;
                         Integrated Security=True"
       providerName="System.Data.SqlClient" />
  <add name="{Prod}NorthwindConnectionString"
       connectionString="Data Source=ProdServer;Initial Catalog=Northwind;
                         Integrated Security=True"
       providerName="System.Data.SqlClient" />
</connectionStrings>

Everything else in the website or the web application can remain the same and be used as it would be expected to be.

Implementation

The two classes are created in a similar way - they consist of the code in the standard AppSettings and ConnectionStrings Expression builders modified slightly. I got the code by using Reflector.

Firstly, though, I created a small helper class with a couple of static methods which are used by both of the other two classes. The first method determines whether we are in Dev / QA / Prod etc., by looking at the SERVER_NAME server variable, and then looks through all the AppSettings with keys starting in "ServerRegion". It then returns the braces and the name inside them for the match:

internal static string ServerRegion()
{
  string whichServer = 
    HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
  string whichServerRegion = null;

  // Loop through all the AppSettings, looking for ServerRegion Keys
  // and then find a match against our server
  foreach (string key in ConfigurationManager.AppSettings.AllKeys)
  {
    if (key.StartsWith("ServerRegion", StringComparison.CurrentCultureIgnoreCase))
    {
      if (String.Compare(whichServer, ConfigurationManager.AppSettings[key], true) == 0)
      {
        Regex regex = new Regex("\\{(?<textinsidebrackets />\\w+)\\}");
        Match match = regex.Match(key);
        if (match.Success)
        {
          whichServerRegion = match.Groups["TextInsideBrackets"].Value;
          break;
        }
      }
    }
  }

  if (whichServerRegion == null)
    throw new InvalidOperationException("No Server Region Keys" + 
                                        " matching current server name");

  return "{" + whichServerRegion + "}";
}

The other method makes use of the server region and combines it with the appSetting key to find a setting which can be returned. Thus, it will look for keys set up as Region + Key, or Key + Region and finally Key. The last is available so that we can have a single setting common to all landscapes.

internal static string GetAppSetting(string key)
{
  string region = ServerRegion();
  string str;

  str = ConfigurationManager.AppSettings[region + key];
  if (str == null)
  {
    str = ConfigurationManager.AppSettings[key + region];
    if (str == null)
    {
      str = ConfigurationManager.AppSettings[key];
    }
  }
  return str;
}

Within the AppSetting Expression Builder, we then change the method GetAppSetting to return our helper's GetAppSetting. I left all the remaining methods untouched as I found them in Reflector.

public static object GetAppSetting(string key)
{
  string str = Helper.GetAppSetting(key);
  if (str == null)
  {
    throw new InvalidOperationException("AppSetting_not_found");
  }
  return str;
}

The ConnectionString Expression Builder does something very similar.

Points of interest

Is this the right way to get what I need? Possibly not. I have long since learned that the chances of my finding something which hasn't been done before are pretty small. However, I have been searching on and off for something like this for a long time. So, for the time being, it is the best I have. I would welcome comments, but please don't flame me too hard.

One limitation which I have come across is that of the use of any of the Membership / Role Providers which are declared in web.config. They don't seem to use the Expression Builder's version of the Connection String, preferring to get it directly. I don't make a lot of use of either, preferring to use Windows authentication and Group membership to determine access. However, I have been working on cut down versions of both the Membership and Role Providers, and I will gear them up to use the Expression built Connection Strings instead.

History

  • 25 March 2009: First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here