Es herrschen immer wieder Unklarheiten darüber, wie das mit der app.config unter .NET genau funktioniert. Dabei gibt es eigentlich nur ein paar Sachen die man dazu wissen muss.
Der wichtigste Punkt, der einem klar sein muss ist, dass es dabei verschiedene Konfigurations-Modelle gibt. Um genau zu sein, gibt es zwei davon. Einmal die AppSettings und dann die ApplicationSettings *bzw. UserSettings*. Hört sich erst einmal ziemlich gleich an, aber im Grunde sind es einfach zwei verschiedene Sachen und genau das ist das Problem, dass viele einfach diesen Unterschied nicht kennen.

Nur eines haben die beiden Modelle gemeinsam: Man braucht eine app.config, wobei diese Datei beim Kompilieren automatisch umbenannt wird in *.exe.config*. Es handelt sich dabei um eine ganz normale XML-Datei

AppSettings

Dies ist wohl die einfachste Art der Konfiguration. Wenn ich mich recht erinnere ist das auch das ursprüngliche Konfigurations-Modell in .NET. Wie funktioniert das? Dazu legt man sich erst einmal eine app.config an (Hinzufügen -> Neues Element -> Anwendungskonfigurationsdatei). Wenn man jetzt diese Datei öffnet, steht da erst einmal noch nicht viel drin.

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

Das Wichtige kommt jetzt in den Tag ** rein. Und wenn man da das Schreiben anfängt, schlägt das Intellisense von Visual Studio auch gleich die wichtigen Sachen vor: Ganz oben steht ein Eintrag mit ** und das ist genau das, was wir brauchen.

Eine mögliche Konfiguration sieht jetzt folgendermaßen aus:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="language" value="de-DE"/>
  </appSettings>
</configuration>

Das ist ja schon mal nicht schwer. Interessanter ist aber, wie man auf dieses Setting jetzt zugreifen kann.

Dazu muss man erst einen zusätzlichen Verweis auf die Assembly System.configuration hinzufügen. Diese wird benötigt, weil sich darin der ConfigurationManager befindet, der uns letztendlich die ganze Arbeit mit dem Parsen des XML-Files abnimmt. Dann ist der Zugriff nur noch ein Kinderspiel:

string language = ConfigurationManager.AppSettings["language"];

Das ist schon die ganze Kunst. Aber natürlich hat dieses Konfigurations-Modell auch einen Nachteil. Die Settings liegen in der app.config, die im gleichen Verzeichnis liegen muss, wie die Anwendung selbst. Eine Anwendung befindet sich normalerweise im Verzeichnis c:\programme bzw. c:\program files und aus Sicherheitsgründen unterbindet Microsoft einem eingeschränkten Benutzer das Schreiben in einem solchen Verzeichnis. Das heißt also, dass nur Administratoren die Anwendungskonfiguration ändern können. Man hat auch keine direkte Möglichkeit, die Konfiguration vom Programm aus zu ändern (wobei man da ohnehin auch die oben genannten Probleme laufen würde). Man kann natürlich das XML selbst parsen und ändern, aber das wird dann schon ziemlich aufwändig.

Schöner ist da das zweite Konfigurations-Modell, das .NET bietet:

ApplicationSettings

Ich habe oben geschrieben, dass man in beiden Fällen eine app.config benötigt, um eines der Konfigurations-Modelle (oder beide) zu nutzen. Ich behaupte jetzt aber, dass wir hier erst einmal keine app.config anlegen. Stattdessen klicken wir mit der rechten Maustaste auf das Projekt und öffnen die Projekt-Eigenschaften. Hier finden wir einen Reiter „Einstellungen„. Wenn man den anklickt erhält man die Meldung „Dieses Projekt enthält keine Standardeinstellungsdatei. Klicken Sie hier um eine zu erstellen„. Wenn man da drauf klickt, kommt man zu einem Editor, in dem man Settings anlegen kann. Das machen wir jetzt mal mit dem gleichen Setting, wie im oberen Beispiel

 

Das ist auf jeden Fall schon sehr viel komfortabler als das Editieren einer XML-Datei. Man wird aber bemerken, dass jetzt auch wieder eine app.config im Projekt vorhanden ist (spätestens nachdem man gespeichert hat). Schauen wir uns die mal an:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="ConsoleApplication1.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <applicationSettings>
        <ConsoleApplication1.Properties.Settings>
            <setting name="language" serializeAs="String">
                <value>de-DE</value>
            </setting>
        </ConsoleApplication1.Properties.Settings>
    </applicationSettings>
</configuration>

Da steht jetzt schon um einiges mehr drin als im vorigen Beispiel. Der größte Unterschied ist aber wohl, dass der Tag ** nicht verwendet wird, sondern stattdessen eine ConfigSection mit dem Namen ** eingeführt wurde. Hier finden wir dann auch unser Setting.

Sollten wir unseren oberen Quellcode (ConfigurationManager.AppSettings[„language“]) noch zur Hand haben, werden wir feststellen, dass wir damit nur noch null zurück bekommen. Ist ja auch klar, weil es eben keine appSettings mehr gibt.

Wir greifen wir jetzt stattdessen auf unser Settings zu? Es ist sogar noch einfacher. Visual Studio hat nämlich automatisch eine neue Klasse angelegt ConsoleApplication1.Properties.Settings. Wir finden diese Klasse, wenn man im Projektbaum unter Properties schaut. Da wurde diese Klasse mit dem Dateinamen Settings.Designer.cs angelegt.

Zugreifen kann man daher jetzt ganz einfach:

string language = Properties.Settings.Default.language;

Wenn das so einfach geht, warum nimmt man dann dieses Konfigurations-Modell nicht immer her? Ganz einfach: Die Klasse Settings ist internal. Das heißt, dass man aus einer anderen DLL nicht so einfach darauf zugreifen kann. Man kann das aber auch wieder über den ConfigurationManager machen, aber das ist dann schon aufwändiger:

Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSectionGroup group = configuration.GetSectionGroup("applicationSettings");
ClientSettingsSection section = (ClientSettingsSection)group.Sections[0];
string language = section.Settings.Get("language").Value.ValueXml.InnerText;

Das ist schon nicht mehr so schön. Aber wie man sieht, funktioniert das auch. Die ApplicationSettings haben dafür noch einen ganz anderen Vorteil.

UserSettings

Im Grunde schauen applicationSettings und userSettings vollkommen identisch aus. Der Unterschied ist, dass in der app.config eine weitere ConfigSection angelegt wird, mit dem Namen userSettings und – und das ist das besondere – die UserSettings haben in der settings.designer.cs nicht nur einen Getter, sondern sie haben auch einen Setter und sind damit vom Programm her änderbar.

Properties.Settings.Default.language = "en-US";
Properties.Settings.Default.Save();

So. Wir sind jetzt natürlich neugierig und schauen, was in der app.config jetzt gespeichert wird… Und wir werden feststellen, dass da immer noch „de-DE“ steht. Wenn man das Pogramm aber nochmal ausführt, wird richtigerweise nicht mehr de-DE zurückgegeben, sondern der Wert, den wir im letzten Durchlauf gespeichert haben. Wo wird dieser Wert also abgelegt?

Es wurde folgende Datei angelegt:

C:\user\<benutzername>\appdata\local\ConsoleApplication1\ConsoleApplication....\1.0.0.0\user.config

und der Inhalt sieht folgendermaßen aus:

<?xml version="1.0" encoding="utf-8"?>
  <configuration>
    <userSettings>
      <ConsoleApplication1.Properties.Settings>
        <setting name="language" serializeAs="String">
          <value>en-US</value>
        </setting>
     </ConsoleApplication1.Properties.Settings>
  </userSettings>
</configuration>

Hier ist also unsere Einstellung abgeblieben. Und das ist jetzt auch das besondere: Wir befinden uns nämlich hier in einem Verzeichnis, das vom Benutzer beschreibbar ist. Daher kann man daher eine editierbare Konfiguration erstellen. In der app.config steht lediglich die Defailt-Einstellung für den ersten Start durch einen Benutzer. Wenn das Programm eine user.config findet, wird stattdessen diese verwendet.

Jetzt könnte man meinen, dass dann ja alles gut ist. Aber einen kleinen Stolperstein gibt es noch: Im oben genannten Pfad, in dem man die user.config findet, findet man auch eine Versionsnummer. In meinem Beispielprogramm die 1.0.0.0. Schauen wir mal, was passiert, wenn man die Versionsnummer erhöht. Leider werden wir feststellen, dass jetzt dummerweise wieder „de-DE“ zurückgegeben wird statt des neuen Wertes, den wir in der Version 1.0.0.0 gespeichert haben. Es wurde auch ein neues Verzeichnis mit der neuen Versionsnummer angelegt.

Aber auch dagegen kann man etwas machen, indem man beim Start des Programms folgendes Statement ausführt, um die Einstellungen einer früheren Version in die aktuelle Version zu übernehmen

Properties.Settings.Default.Upgrade();