Click here to Skip to main content
15,867,686 members
Articles / Web Development / ASP.NET

Customising the default AppSettings and ConnectionStrings Expression Builders

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
25 Mar 2009CPOL4 min read 26.9K   90   14   3
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:

XML
<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:

XML
<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:

XML
  <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:

C#
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.

C#
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I have been developing applications for almost 30 years - started with early versions of Basic on the Sinclair ZX80, moving up via UK101 and Amstrad to PC. Played with Assembly as well. First big application was for a complete test set, which was written using QuickBasic (all I had available to me). Moved on to a bit of C and C++ (never really got to grips with them) and then 11 years ago, got into Visual Basic/SQL Server and HTML. Moved onto .NET and then in the last few years changed across to C# when the job market indicated that it would be better.
Have now developed quite a few ASP.NET Applications, and along the way have played with WPF, Silverlight and am currently working on an MVC based application.

Comments and Discussions

 
QuestionGood article Pin
Ashutosh Arya31-Mar-09 9:16
Ashutosh Arya31-Mar-09 9:16 
AnswerRe: Good article Pin
Graham Cottle1-Apr-09 3:47
professionalGraham Cottle1-Apr-09 3:47 
Hi, thanks for your comments - the original plan I used was to have multiple versions of the web.config and then to try and maintain each one. The downside is that I had to remember to do each one and not to copy the development version on top of the production version - then have to copy data from the dev database across to prod when I noticed later. By doing it this way, I kept the number of concurrent file versions down to one - which I can store in Source Safe and not have to worry about overwriting the wrong version when I changed it. Also, with a QA environment and possibly multiple developers each using their own local database as well, it keeps the versioning down a bit. (still an issue with the databases, but nowadays I develop on my own, so it's less of an issue there).
Thanks again
Graham
GeneralRe: Good article Pin
Ashutosh Arya1-Apr-09 4:21
Ashutosh Arya1-Apr-09 4:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.