App.Config CustomSection

App.Config CustomSection

Oft ist es Sinnvoll, die Konfiguration von Komponenten in eine Konfiguration auszulagern. Somit lassen sich die Komponenten auch ohne Code-Anpassungen ändern.
Nun könnte man dazu eigene Dateien anlegen, doch bietet uns das .NET Framework mit der App.Config bereits eine einfache Möglichkeit an, eigene Sektionen zu definieren.
Dieser Artikel soll auf dieses Szenario eingehen.

In meinem Beispiel habe ich einen Dienst, der verschiedene Verzeichnisse in einem angegebenen Intervall durchsuchen soll.
Die Konfiguration dieser Verzeichnisse möchte ich nun über die App.Config vornehmen.

Die Struktur in der App.Config soll in meinem Beispiel wie folgt aufgebaut sein:
<DirectoryScans>
<Directory Path="M:\Movies\" Collection="Filme" />
<Directory Path="M:\Music\" Collection="Musik" />
<Directory Path="M:\TvCollections\" Collection="Serien" />
</DirectoryScans>

Des Weiteren möchte ich einige optionale Eigenschaften für jedes Directory Element anbieten:
Interval, StartImmediatly und Enabled

So könnte ein Eintrag auch wie folgt aussehen:
<Directory Path="M:\Uploads" Collection="Neues" Interval="60" StartImmediatly="False" Enabled="True" />

Nun zur Implementierung!

Implementierung der Elemente

Als erstes müssen wir einen Datentyp für die Directory Einträge implementieren.
Dazu erstellen wir eine Klasse und vererben diese von ConfigurationElement.

Der folgende Code zeigt meine Beispiel-Implementierung.

Ich habe wie gewohnt Eigenschaften implementiert und diese mit dem ConfigurationPropertyAttribute markiert.
Wichtig ist, dass ihr den Name der Eigenschaft angebt und definiert, ob es sich dabei um eine Eigenschaft handelt, die auf jeden Fall gesetzt sein muss (IsRequired).
Insofern ihr IsRequired auf false setzt, solltet ihr auch einen Standardwert über DefaultValue setzen.

Habt ihr eigene Datentypen, so könnt ihr über das TypeConverterAttribute auch dies ermöglichen.

Schaut euch dazu einfach mal die Eigenschaft Interval an. Dort verwende ich den vor implementierten TimeSpanSecondsConverter.
Dadurch kann ich die Eigenschaft in der Konfiguration mit einer Ganzzahl angeben, die den Sekundenwert der TimeSpan Struktur angibt.

public class DirectoryScanElement : ConfigurationElement
{
    #region Properties

    [ConfigurationProperty(nameof(Path), IsRequired = true)]
    public string Path
    {
        get { return this[nameof(Path)].ToString(); }
    }

    [ConfigurationProperty(nameof(Collection), IsRequired = true)]
    public string Collection
    {
        get { return this[nameof(Collection)].ToString(); }
    }

    [ConfigurationProperty(nameof(Interval), DefaultValue = "600", IsRequired = false)]
    [TypeConverter(typeof(TimeSpanSecondsConverter))]
    public TimeSpan Interval
    {
        get { return (TimeSpan)this[nameof(Interval)]; }
    }

    [ConfigurationProperty(nameof(StartImmediatly), DefaultValue = true, IsRequired = false)]
    public bool StartImmediatly
    {
        get { return Convert.ToBoolean(this[nameof(StartImmediatly)]); }
    }

    [ConfigurationProperty(nameof(Enabled), DefaultValue = true, IsRequired = false)]
    public bool Enabled
    {
        get { return Convert.ToBoolean(this[nameof(Enabled)]); }
    }

    #endregion
}

Implementierung der Auflistung

Nachdem wir nun den Datentyp bzw. die Klasse zum halten der Einträge implementiert haben, müssen wir die eigentliche Auflistung dazu implementieren.
Dazu erstellen wir wieder eine Klasse und leiten diese von ConfigurationElementCollection ab.

Im folgenden wieder meine Beispiel-Implementierung.

public class DirectoryScanElementsCollection : ConfigurationElementCollection
{
    #region Methods

    protected override ConfigurationElement CreateNewElement()
    {
        return new DirectoryScanElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((DirectoryScanElement)element).Path;
    }

    #endregion

    #region Properties

    public override ConfigurationElementCollectionType CollectionType
    {
        get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
    }

    #endregion
}

Implementierung der Sektion

Der letzte Schritt ist dann die Implementierung der Sektion.

public class DirectoryScansSection : ConfigurationSection
{
    #region Properties

    [ConfigurationProperty("", IsDefaultCollection = true)]
    [ConfigurationCollection(typeof(DirectoryScanElementsCollection),
        AddItemName = "Directory", ClearItemsName = "Clear", RemoveItemName = "Remove")]
    public DirectoryScanElementsCollection Directories
    {
        get { return (DirectoryScanElementsCollection)base[""]; }
    }

    #endregion
}

Wie ihr sehen könnt, habe ich wieder eine Eigenschaft implementiert und diese mit dem ConfigurationPropertyAttribute markiert.
Zusätzlich habe ich jedoch dieses mal auch das ConfigurationCollectionAttribute angefügt.

Wichtig ist hierbei, insofern ihr nicht die gewohnten Namen add, remove und clear für das hinzufügen, entfernen und leeren der Einträge verwenden wollt, solltet ihr diese überschreiben bzw. angeben.
In meinem Fall möchte ich für das hinzufügen, Directory nutzen.

Registrieren der CustomSection

Damit .NET eure eigene Sektion kennt, müsst ihr diese zuvor in der App.Config Datei registrieren.
Dazu fügt ihr ganz oben in der Datei den Abschnitt configSections hinzu.

<configSections>
<section name="<SectionName>"
type="<FullNamespace>.<SectionClassName>, <AssemblyName>" />
</configSections>

Insofern ihr alles Richtig gemacht habt, könnt ihr nun im Code die CustomSection auslesen:

Verwendung im Code

Die Sektion könnt ihr über ConfigurationManager.GetSection() ermitteln:

var section = (DirectoryScansSection)ConfigurationManager.GetSection("DirectoryScans")

Über die einzelnen Einträge könnt ihr dann iterieren:

foreach (DirectoryScanElement directory in section.Directories)
{
    string path = directory.Path;
    string collection = directory.Collection;
    TimeSpan interval = directory.Interval;
    bool startImmediatly = directory.StartImmediatly;
    bool enabled = directory.Enabled;
}
Um einen Kommentar zu hinterlassen, ist eine Anmeldung erforderlich.