20 Dec
2006

AppSettings und die Entwicklung im Team

 

Neulich hatte ich mit Thomas eine kurze Unterhaltung über die Problematik der Team-Entwicklung. Ein Problem sind die verschiedenen Einstellungen für die AppSettings und ConnectionStrings. Nun wurde dies auch bei mir akut und bin zur folgenden Lösung gekommen. Ich hatte schon vorher den Zugriff auf die AppSettings gekapselt, so habe ich nie direkt auf die AppSettings zugegriffen, sondern über statische Methode einer Klasse. Also nicht

string cn = ConfigurationManager.AppSettings["Database.ConnectionStringName"];

sondern

public class MySettings
{
    public static string ConnectionStringName
    {
        get
        {
            return ConfigurationManager.AppSettings["Database.ConnectionStringName"];
        }
    }
}

In Realität ist es ein wenig aufwendiger, so gab es Standardwerte, Typumwandlungen usw.

Nun gut, dies löst jedoch nicht direkt das Problem der Team-Entwicklung wenn z. B. verschiedene Entwickler verschiedene ConnectionStrings oder andere Angaben brauchen. Entwickler spezifische Settings-Dateien haben den Nachteil dass diese z.B. nicht in der Quellcode-Verwaltung zu landen oder dass man damit irgendwann einfach durcheinander kommt, da irgendwo die eigentliche Datei ja doch eingebunden werden muss. Deshalb nutze ich weiterhin den Ansatz von gemeinsamen Konfigurationsdateien, diese können jedoch Entwickler spezifische Einträge enthalten. Dazu muss der Entwickler nur identifiziert werden können, dies kann z. B. über den Benutzernamen oder den Namen des Rechner geschehen. Dies kann dann in der Konfiguration so ausehen.

<appSettings>
    <add key="Database.ConnectionStringName" value="MyDataBase"/>
    <add key="Database.ConnectionStringName.M00072348" value="MyDataBase.M00072348"/>
    <add key="Database.ConnectionStringName.M00072349" value="MyDataBase.M00072349"/>
    <add key="WeeksToDisplay" value="5"/>
    <add key="WeeksToDisplay.M00072348" value="2"/>
    <add key="Database.CacheExpirationTime" value="1.5"/>
</appSettings>
[...]
<connectionStrings>
    <add name="MyDataBase" connectionString="DefaultConnString"/>
    <add name="MyDataBase.M00072348" connectionString="DefaultConnStringForDev1"/>
    <add name="MyDataBase.M00072349" connectionString="DefaultConnStringForDev2"/>
</connectionStrings>

Zusätzlich zu den Standard-Settings existieren dann, bei Bedarf, zusätzliche Einträge denen einfach der Name des Rechners angehangen ist. Den Zugriff darauf natürlich wieder gekapselt, so dass der Zugriff bei der Anwendung Transparent abläuft. Dazu habe ich eine ApplicationSettings Klasse für C# 2.0 entwickelt die mir die Arbeit abnimmt. Diese sieht so aus (die vollständige Implemetation befindet sich weiter unten)

public abstract class ApplicationSettings
{
    protected static ApplicationSettings Instance
    {
        get;
    }    
    
    protected static void Initialize<T>(string identifier) 
          where T : ApplicationSettings;
    
    protected virtual string SettingsPrefix
    {
        get;
    }
    
    public T Get<T>(string settingsKey, T defaultValue);
}

Umgemünzt auf mein genanntes Beispiel von oben sieht dann meine Implementation von MySettings nun so aus.

public class MySettings : ApplicationSettings
{
    static MySettings()
    {
        Initialize<MySettings>(Environment.MachineName);
    }
    public static string ConnectionStringName
    {
        get { return Instance.Get("Database.ConnectionStringName", "DefaultConnString");}
    }
    public static int WeeksToDisplay
    {
        get { return Instance.Get("WeeksToDisplay", 6); }
    }
    public static double CacheExpirationTime
    {
        get { return Instance.Get("Database.CacheExpirationTime", 0.25);}
    }
}

Beim Initialize() könnte man auch Environment.UserName übergeben, dies hängt dann von den Anforderungen ab. Die SettingsPrefix Eigenschaft kann man noch überschreiben wenn man noch einen Prefix für alle Einstellungen vor dem Namen des Keys braucht.

string cn = MySettings.ConnectionStringName;
int weeks = MySettings.WeeksToDisplay;
double time = MySettings.CacheExpirationTime;

Mit meiner ApplicationSettings-Klasse hat man einen einfachen, typsicheren Zugriff auf die AppSettings. Hier nun der Code.

public abstract class ApplicationSettings
{
    private static ApplicationSettings instance;
    private static object instanceLock = new object();

    private string identifier;
    
    protected static ApplicationSettings Instance
    {
        get
        {
            return instance;
        }
    }
    
    protected static void Initialize<T>(string identifier) 
                      where T : ApplicationSettings
    {
        if (instance == null)
        {
            lock (instanceLock)
            {
                if (instance == null)
                {
                    instance = (ApplicationSettings) 
                                Activator.CreateInstance(typeof (T));
                    instance.identifier = identifier;
                }
            }
        } 
    }

    private object GetSettingValue(string settingsKey, object defaultValue)
    {
        NameValueCollection settings = ConfigurationManager.AppSettings;

        if (SettingsPrefix.Length > 0)
        {
            settingsKey = string.Format("{0}.{1}", SettingsPrefix, settingsKey);
        }

        string keyName = string.Format("{0}.{1}", settingsKey, identifier);

        if (settings[keyName] != null)
        {
            return settings[keyName];
        }
        else if (settings[settingsKey] != null)
        {
            return settings[settingsKey];
        }
        else
        {
            return defaultValue;
        }
    }

    protected virtual string SettingsPrefix
    {
        get { return string.Empty; }
    }

    public T Get<T>(string settingsKey, T defaultValue)
    {
        return (T)Convert.ChangeType(GetSettingValue(settingsKey, 
                                             defaultValue), 
                                     typeof(T), 
                                     CultureInfo.InvariantCulture);
    }
}

Der Eintrag ist mir etwas Wert
 
Comments have been closed on this topic.