Dennis Burton's Develop Using .NET

Change is optional. Survival is not required.
Tags: Azure

This is one of those examples of where demo code and samples diverge from the code you use in a real application. For almost every example you see for loading configuration settings, like connection strings, you will see the following code:

RoleEnvironment.GetConfigurationSettingValue(key)

This code will pull the value in the service configuration file associated with the given key. This would be sufficient if we had the same tools for a service configuration that we had for web.config files. For web.config files, we have a set of XSL files that can be used to apply different settings for different environments. One common place this occurs is in connection strings. In the real world, we set up the configuration strings for production, staging and development environments once and use the config file generated for the current environment.

Adapting config in Azure

Until the XSL tools are available for the service configuration, this technique and helper class can reduce the amount of work you have to perform on your service configuration when changing environments during the development cycle. First, add an entry in your service configuration to indicate your current environment. I use values of Development, Staging, and Production for this setting. Then for your entries that have a different value between the different environments, create a setting with the original setting name prefixed with the environment name. The image below shows how this would look for the setting ConnString.

EnvironmentBasedConfigSettings

The AzureConfigurationSettings class (shown below) will provide the functionality for the CurrentEnvironment setting to be used for determining which of the ConnString values should be used for the current instance. Using this utility, all of the appropriate connections strings may be kept in the service configuration and only one setting needs to change when switching environments. In order to consume this in your code, use AzureConfiguraitonSettings instead of RoleEnvironment when reading configuration settings.

AzureConfigurationSettings.GetConfigurationSettingValue(key);

It is worth noting that Alex Lambert has found a way to run the configuration transforms on service configuration files. He documents the process in a blog post. A side effect of this is that you lose some ability to use the visual tools for editing configuration. There is however much less noise in the configuration file than the approach in this blog post. Until support for configuration transforms is added to Visual Studio, you will have to pick your pain points.

How it works

The AzureConfigurationSettings class will first attempt to use the configuration setting without the decoration for current environment. This ensures that code you have today will function the same. If no value was found without decoration, the code will attempt to use the configuration setting prefixed with the string found in the CurrentEnvironment setting. I also handled the RoleEnvironment.Changing event so that if a new configuration were loaded through the portal with the CurrentEnvironment setting changed, the cache of that value would be invalidated.

The Source

The code you see below may also be found on bitbucket with the rest of the solution.

public class AzureConfigurationSettings
{
  private const string CurrentEnvironmentKey = "CurrentEnvironment";
  private readonly static object lockObj = new object();

  static AzureConfigurationSettings()
  {
    RoleEnvironment.Changing += (sender, e) => {
        var hasCurrentEnvironmentChanged = e.Changes
          .OfType<roleenvironmentconfigurationsettingchange>()
          .Any(change => change.ConfigurationSettingName == CurrentEnvironmentKey);

        if (hasCurrentEnvironmentChanged)
        {
          lock (lockObj)
          {
            isCurrentEnvironmentLoaded = false;
          }
        }
      };
  }

  public static string GetConfigurationSettingValue(string key)
  {
    string value;
    if (TryGetValue(key, out value))
      return value;

    return null;
  }

  private static bool isCurrentEnvironmentLoaded = false;
  public static string currentEnvironment;
  public static string CurrentEnvironment
  {
    get
    {
      if (!isCurrentEnvironmentLoaded)
      {
        lock (lockObj)
        {
          if (!isCurrentEnvironmentLoaded)
          {
            try
            {
              currentEnvironment = RoleEnvironment
                .GetConfigurationSettingValue(CurrentEnvironmentKey);
            }
            catch (RoleEnvironmentException)
            {}
            isCurrentEnvironmentLoaded = true;
          }
        }
      }
      return currentEnvironment;
    }
  }

  private static bool TryGetValue(string key, out string value)
  {
    value = null;
    try
    {
      value = RoleEnvironment.GetConfigurationSettingValue(key);
    }
    catch (RoleEnvironmentException)
    {
      try
      {
        if(!string.IsNullOrEmpty(CurrentEnvironment))
          value = RoleEnvironment.GetConfigurationSettingValue(CurrentEnvironment + key);
      }
      catch (RoleEnvironmentException)
      {
        return false;
      }
    }

    return true;
  }
}
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview

Dennis Burton

View Dennis Burton's profile on LinkedIn
Follow me on twitter
Rate my presentations
Google Code repository

Community Events

Windows Azure Boot Camp Lansing GiveCamp