ASP.NET MVC: Eigenen benutzerdefinierten Bereich in der Web.config Datei erstellen

ASP.NET MVC: Eigenen benutzerdefinierten Bereich in der Web.config Datei erstellen

Mit der Web.config bietet .NET eine zentrale Konfigurationsdatei für verschiedene Optionen der jeweiligen Anwendung. So kann beispielsweise die Ziel-Version des .NET Frameworks festgelegt werden. Spätestens bei etwas komplexeren Anwendungen wird man den Bedarf feststellen, gewisse Parameter zur Laufzeit verändern zu wollen. Dies erhöht die Flexibilität und Wartbarkeit gegenüber hart kodierten Werten. Warum die eigenen Konfigurationsparameter nicht gleich in die bereits existierende Web.config einbinden? Damit fällt das suchen in verschiedenen Dateien weg, da alles zentral in einer Konfigurationsdatei definiert ist. Dieser Artikel zeigt, wie man eigene Parameter in der Datei unterbringt und auf sie zugreift.

Simple Schlüssel-Wert Paare speichern und auslesen

Am einfachsten funktioniert dies mit Schlüssel-Wert Paaren oder auch KeyValue-Pair genannt. Man hat in diesem Fall also nur einen einzigen benannten Schlüssel, dem ein bestimmter Wert zugewiesen wird. Beispielsweise könnte man so einen Debug-Modus aktivieren oder einen Basis-Host definieren, der für bestimmte Abfragen (etwa an eine API) verwendet wird. Im <config> Knoten suchen wir dafür nach <appSettings>. Je nach Anwendungstyp ist dieser bereits vorhanden und mit einigen Schlüsselpaaren gefüllt, etwa in ASP.NET Webanwendungen. Falls nicht, kann dieser als leerer XML-Tag erstellt werden:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
  </appSettings>
</configuration>

In den appSettings können wir nun mit dem add Element ein Schlüsselpaar hinzufügen:

<appSettings>
  <add key="enableDebugOutput" value="true" />
</appSettings>

Schlüssel (key) und Wert (value) können hierbei frei gewählt werden. Im Code der Anwendung lassen sich die Werte über die ConfigurationManager-Klasse auslesen, wozu der Namensraum System.Configuration importiert werden muss:

bool debugModeEnabled = Convert.ToBoolean(ConfigurationManager.AppSettings["enableDebugOutput"]);

Das Umwandeln in den jeweiligen Zieltyp wird auf diese Art jedoch spätestens dann lästig, wenn man mehrere Integer- bzw. Boolean-Werte auslesen möchte. Leider können wir die ConfigurationManager-Klasse nicht mit einer Erweiterungsmethode ergänzen, da sie statisch ist. Daher ist eine kleine Hilfsklasse nötig:

using System.ComponentModel;
using System.Configuration;

public static class AppSettings {
    /// <summary>
    /// Gibt den Wert eines Schlüssels im appSettings-Knoten der Web.config stark typisert zurück oder wirft eine Exception, wenn der Wert nicht vorhanden bzw. leer ist. 
    /// </summary>
    /// <typeparam name="T">Typ des Rückgabewertes</typeparam>
    /// <param name="key">Schlüssel des KeyValue-Pairs in der Konfigurationsdatei</param>
    /// <returns></returns>
    public static T Get<T>(string key) {
        var appSetting = ConfigurationManager.AppSettings[key];
        if (string.IsNullOrWhiteSpace(appSetting)) {
            var exceptionMessage = string.Format("Der Schlüssel '{0}' wurde im appSettings-Abschnitt nicht gefunden oder besitzt einen leeren Wert!", key);
            throw new ConfigurationErrorsException(exceptionMessage);
        }

        var converter = TypeDescriptor.GetConverter(typeof(T));
        return (T)(converter.ConvertFromInvariantString(appSetting));
    }
}

Damit wird der Aufruf schon deutlich übersichtlicher:

bool debugModeEnabled = AppSettings.Get<bool>("enableDebugOutput");

Um den Scope möglichst gering zu halten und die Fehleranfälligkeit zu verringern, empfiehlt sich zudem eine Wrapper-Klasse:

public static class MySettings {
    public static bool DebugModeEnabled {
        get {
            return AppSettings.Get<bool>("enableDebugOutput");
        }
    }
}

Eigene Bereich erstellen

Spätestens wenn man mehrere zusammengehörende Werte speichern möchte, ist die Lösung über Schlüssel-Wert Paare nicht optimal. Doch daran wurde in .NET ebenfalls gedacht: Man kann ähnlich einfach benutzerdefinierte Bereiche (sogenannte Sections) erstellen, denen sich beliebige Eigenschaften zuweisen lassen. Dazu erstellen wir zunächst eine Wrapper-Klasse, die von ConfigurationSection erbt:

using System.Configuration;

public class DebugOptionsSection : ConfigurationSection {

    [ConfigurationProperty("enableDebugOutput")]
    public bool EnableDebugOutput {
        get { return (bool)this["enableDebugOutput"]; }
    }

    [ConfigurationProperty("debugLogFile")]
    public string DebugLogFile {
        get { return (string)this["debugLogFile"]; }
    }
}

In der Web.config muss dieser Wrapper nun in den configSections registriert werden:

[csharp]
using System.Configuration;

public class DebugOptionsSection : ConfigurationSection {

    [ConfigurationProperty("enableDebugOutput")]
    public bool EnableDebugOutput {
        get { return (bool)this["enableDebugOutput"]; }
    }

    [ConfigurationProperty("debugLogFile")]
    public string DebugLogFile {
        get { return (string)this["debugLogFile"]; }
    }
}
[/csharp]
<configuration>
  <configSections>
    <section name="debugOptionsSection" type="ULabs.DebugOptionSection" />
  </configSections>
</configuration>

Zu beachten ist, dass in der type Eigenschaft der vollqualifizierte Name angegeben werden muss – Also im Format Namespace.Klasse!

Anschließend können wir den Bereich selbst definieren und entsprechend unseres Wrappers mit den benötigten Eigenschaften ausstatten:

<configuration>
  <debugOptionsSection enableDebugOutput="true" debugLogFile="debug.log" />
</configuration>

Zu guter Letzt wollen wir die dort angegebenen Werte natürlich noch codeseitig auslesen. Über die GetSection Methode des Konfigurationsmanagers erhalten wir eine Instanz dieser Wrapper-Klasse und können auf die Attribute zugreifen:

var debugConfig = (DebugOptionsSection)ConfigurationManager.GetSection("debugOptionsSection");
if(debugConfig.EnableDebugOutput) {
    // Debug-Ausgabe aktiviert
}

Der Vorteil besteht hierbei in der möglichen Verfeinerung der Konfiguration über DataAnnotiation-Attribute. Beispielsweise können wir im ConfigurationProperty mit der Eigenschaft DefaultValue einen Standardwert festlegen, wodurch wiederholende Prüfungen und Zuweisungen vermieden werden:

    [ConfigurationProperty("enableDebugOutput", DefaultValue = false)]
    public bool EnableDebugOutput {
        get { return (bool)this["enableDebugOutput"]; }
    }

Auch umfangreichere Validierungen sind möglich. So lässt sich beispielsweise die Länge von Strings begrenzen oder ihr Inhalt mit regulären Ausdrücken prüfen. Dies weiter auszuführen würde den Rahmen dieses Artikels jedoch etwas sprengen. Außerdem gibt es diesbezüglich bereits einen guten Artikel in der MSDN von Microsoft: Erstellen von benutzerdefinierten Konfigurationsabschnitten mit ConfigurationSection

Leave a Reply