0

Given an Azure Cloud Service, is it possible to use a settings.setting file instead of storing them in <appSettings>?

I have a project like:

  • MyApp.Core - DLL
    • WorkerRole.cs
    • app.config
  • MyApp.Core.AzureService
    • ServiceConfiguration.Prod.cscfg
    • ServiceConfiguration.Staging.cscfg

The app.config contains a appSettings like <add key="clientId" value="Dev"/>

Then the two cscfg files can override this setting to be azure environment specific. For example, Prod.cscfg:

<ConfigurationSettings>
  <Setting name="clientId" value="Prod" />
</ConfigurationSettings>

Then in Azure I can further customize these settings:

enter image description here

Can I switch to use a settings.setting file and still allow azure to wire up the overrides correctly?

Philip Pittle
  • 11,821
  • 8
  • 59
  • 123

1 Answers1

0

After playing with this for a while I decided to come up with a wrapper class to handle both scenarios.

It's used like this:

public class Example
{
     private readonly ICloudSettingsProvider _cloudSettingsProvider;

     public void DoWork(){
         var setting = __cloudSettingsProvider.GetConfigurationSetting(
                        () => Properties.Setting.Default.ExampleConfiguration);

     }
}

Implementation:

public class CloudSettingsProvider : ICloudSettingsProvider
{
    public T GetConfigurationSetting<T>(Expression<Func<T>> setting)
    {
        Guard.ArgumentNotNull(setting, "setting");

        var settingNames = new List<string>();
        T settingDefaultValue;

        #region Parse settingNames / settingDefaultValue from setting Expression
        try
        {
            var memberExpression = (MemberExpression) setting.Body;

            //////Setting Name
            var settingName = memberExpression.Member.Name;

            if (string.IsNullOrEmpty(settingName))
                throw new Exception("Failed to get Setting Name (ie Property Name) from Expression");

            settingNames.Add(settingName);

            //////Setting Full Name
            var memberReflectedType = memberExpression.Member.ReflectedType;

            if (null != memberReflectedType)
                //we can use the settings full namespace as a search candidate as well
                settingNames.Add(memberReflectedType.FullName + "." + settingName);

            //////Setting Default Value
            settingDefaultValue = setting.Compile().Invoke();
        }
        catch (Exception e)
        {
            #region Wrap and Throw
            throw new Exception("Failed to parse Setting expression.  " +
                                "Expression should be like [() => Properties.Setting.Default.Foo]: " + e.Message,
                                e);
            #endregion
        }
        #endregion

        return GetConfigurationSettingInternal(
            settingDefaultValue,
            settingNames.ToArray());
    }      

    private T GetConfigurationSettingInternal<T>(T defaultvalue = default(T), params string[] configurationSettingNames)

    {
        Guard.ArgumentNotNullOrEmptyEnumerable(configurationSettingNames, "configurationSettingNames");

        //function for converting a string setting found in the 
        //<appConfig> or azure config to type T
        var conversionFunc = new Func<string, T>(setting =>
        {
            if (typeof(T).IsEnum)
                return (T)Enum.Parse(typeof(T), setting);

            if (!typeof(T).IsClass || typeof(T) == typeof(string))
                //value type
                return (T)Convert.ChangeType(setting, typeof(T));

            //dealing with a complex custom type, so let's assume it's
            //been serialized into xml. 
            return
                setting
                    .UnescapeXml()
                    .FromXml<T>();
        });


        ///////////////
        // Note: RoleEnvironment isn't always available in Unit Tests
        //   Fixing this by putting a general try/catch and returning the defaultValue
        //////////////
        try
        {
            if (!RoleEnvironment.IsAvailable)
            {
                //Check the <appSettings> element.
                var appConfigValue =
                    configurationSettingNames
                        .Select(name => ConfigurationManager.AppSettings[name])
                        .FirstOrDefault(value => !string.IsNullOrEmpty(value));

                return !string.IsNullOrEmpty(appConfigValue)
                    ? conversionFunc(appConfigValue)
                    : defaultvalue;

            }

            //Note: RoleEnvironment will fallback to <appConfig>
            //http://stackoverflow.com/questions/11548301/azure-configuration-settings-and-microsoft-windowsazure-cloudconfigurationmanage
            var azureConfigValue =
                configurationSettingNames
                        .Select(name => RoleEnvironment.GetConfigurationSettingValue(name))
                        .FirstOrDefault(value => !string.IsNullOrEmpty(value));

            return !string.IsNullOrEmpty(azureConfigValue)
                    ? conversionFunc(azureConfigValue)
                    : defaultvalue;

        }
        catch (InvalidCastException e)
        {
            _Logger.Warning(
                string.Format(
                    "InvalidCastException getting configuration setting [{0}]." +
                    "This generally means the value entered is of the wrong type (ie " +
                    "it wants an Int, but the config value has non-numeric characters: {1}",
                        configurationSettingNames, e.Message), e);
        }
        catch (Exception e)
        {
            _Logger.Warning(
                string.Format(
                    "Unhandled Exception getting configuration setting [{0}]: {1}",
                        configurationSettingNames, e.Message), e);
        }

        //If we are here, we hit an exception so return default value
        return defaultvalue;
    }
Philip Pittle
  • 11,821
  • 8
  • 59
  • 123