views:

389

answers:

7

I searched here for the answer. I'm sorry if this has been asked before (as I suspect it has).

Summary: How can I have strongly typed calls into my web.config without duplicating the property names?


Details: In my code, I try to minimize string usage, and I don't like defining something twice.

Complementing both of these wonts is my restriction of AppSettings usage (and its strings) to one class, which I reference throughout the project. The AppSettings class exposes public properties:

    12   public static string DateFormatString {
    13       get {
    14           return ConfigurationManager.AppSettings["DateFormatString"];
    15       }
    16   }

How can I keep this class and prevent the duplication (lines 12 & 14) of the property name?

Alternatively, what other solution might you recommend?

A: 

I like using Custom Configuration Handlers.

http://haacked.com/archive/2007/03/12/custom-configuration-sections-in-3-easy-steps.aspx

Talljoe
A: 

I'd probably recommend deserializing the web.config into an object. You can then access all configuration entries as if they were properties (can't get much more strongly typed than that!)

Jason Watts
How does this fit with my need to be strongly typed? To what object type would I deserialize?
lance
A: 

One solution could be,

public enum Settings
{
    None,
    DateFormatString,
    DefeaultUserPrefix,
    SomeOtherValue
}

and then have a helper class like,

public static class ConfigurationHelper
{
     public static Get<T>(Settings setting)
     {
         string output = ConfigurationManager.AppSettings[setting.ToString()];
         if(string.isNullOrEmpty(output))
               throw new ConfigurationErrorsException("Setting " + setting + " is not defined in Configuration file.");
         return (T)output; //You can probably use Convert.* functions. 
     }
}

Your calling code will look like,

ConfigurationHelper.Get<string>(Settings.DateFormatString);

The above approach provides some level of strong typing but you still have to make sure that the settings name in config file matches with the name of the enum.

The best choice would be to auto-generate class based on the configuration file.


If you wanted strongly typed properties, you can write

public static string DateFormatString
{
    get { return ConfigurationHelper.Get<string>(Settings.DateFormatString); }
}

Also, I would advice against reading the config file in constructor (premature optimization?). If you read the config file in constructor, it means that you can not change config file while application is running.

SolutionYogi
A: 

I create a wrapper for everything that doesn't belong to me directly. This includes cache, session, configuration, external web services, etc. This allows me to encapsulate the dirty details of using that widget. In the case of configuration I have a bare bones configuration class that exposes the various properties that I have housed my app.config or web.config. This might look something like this:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Services.Impl;
using StructureMap;

namespace RanchBuddy.Core.Services.Impl
{
    [Pluggable("Default")]
    public class ConfigurationService : IConfigurationService
    {
        internal static int GetDefaultCacheDuration_Days()
        {
            return Convert.ToInt32(ConfigurationManager.AppSettings["DefaultCacheDuration_Days"]);
        }

     ...

        internal static LoggingType GetLoggingType()
        {
            string loggingType = ConfigurationManager.AppSettings["LoggingType"].ToString();
            if(loggingType.ToLower() == "verbose")
            {
                return LoggingType.Verbose;
            }
            else if (loggingType.ToLower() == "error")
            {
                return LoggingType.Error;
            }
            return LoggingType.Error;
        }

     ...

        public static string GetRoot()
        {
            string result = "";
            if(ConfigurationManager.AppSettings["Root"] != null)
            {
                result = ConfigurationManager.AppSettings["Root"].ToString();
            }
            return result;
        }
    }
}

Inside here you can do more than simply get values from the config file. You can convert a string to the type that it needs to be. You can use the string to determine an enum value to be returned. Etc. But the key is that any changes that ever need to be made regarding configuration can be made here. To include if you want to swap out the mechanism for the configuration store!

Andrew Siemer
A: 

Build your own ConfigurationSection with the Configuration Section Designer, this way you don't have to use AppSettings at all... but you'll have your own collection of settings that will even have Intellisense in the web.config file.

John Rasch
A: 

There's no duplication in your example: One DateFormatString is a property name and the other is a string. You're just following a convention which names the property identically to the lookup key for the value.

I do see one possible improvement. You should read the config file once in a static constructor and store the values instead of reading them from AppSettings every time a property is accessed.

Jamie Ide
Great point about the once/constructor. Regarding "no duplication", I don't want to edit the code in two places when I rename one of the properties in the web.config.
lance
You could use settings in a resource file instead of web.config. Enter settings on the Settings tab of the project's property pages.
Jamie Ide
A: 

This project does the job brilliantly, as part of a standard compile http://www.codeproject.com/KB/cs/genuilder.aspx - no magic strings at all

mcintyre321