HowTo: Globales web|app Konfigurationsfile in mehreren Projekten nützen
Will man eine Konfigurationsdatei in mehreren Projekten gemeinsam nützen, ist das von Haus aus nicht ganz so einfach bzw. wird “out of the box” von Visual Studio nicht unterstützt. Bei einem meiner aktuellen Projekte steh ich aber genau vor dieser Herausforderung. Ich hab als Ausgangsitutation folgende Projektstruktur:
- Projekt: Services (WCF Services)
- Projekt: Richt Client (WPF Client)
- Projekt: Core Lib
Die beiden ersten Projekte sind voneinander unabhängig und werden von unterschiedlichen Stellen aus verwaltet. Beide verwenden aber die _Core Lib_ in der sich logischerweise einiges an Funktionalität befindet, die von den beiden anderen Projekten verwendet wird. Zum Beispiel befindet sich darin der DAL, der ja, verwendet man als Datenspeicher ein RDMS, einen Connectionstring benötigt. Genau diese Konfigurationseinstellung will man jetzt nicht bei den beiden “Hauptprojekten” replizit speichern sondern zentral verwalten können und auf die beiden Projekte automatisch verteilen. Aber wie geht das Ganze?
Meines Wissens nach gibt es von Visual Studio dafür keine “out of the box” Lösung, um genau so ein Szenario abzubilden. Es ist zwar möglich, auf die web|app.config eines anderen Projektes zuzugreifen — was aber bedeutet, das seine Konfigurationseinstellungen dezentral verwaltet werden. Und genau das will ich normalerweise in einem größeren Projekt vermeiden.
Lösung? Verwendung von Pre/Post Build Events!
Visual Studio macht es einem auf “High Level” Basis relativ einfach, den Build-Vorgang zu erweitern. Um nicht in die Tiefe gehen zu müssen, und benutzererstellte MSBuild Tasks zu erstellen, gibt es für jedes VS Projekt die sogenannten Build Events. Mit denen ist es möglich, vor und nach einem Kompiliervorgang gezielt eigene Kommandos auszuführen. Zum Beispiel ist es möglich, die alt bekannten DOS Kommandos wie copy oder mkdir auszuführen. Und genau das machen wir uns in diesem Fall zu nütze.
Über die Projekt-Einstellungen kommt man zu folgenden Screen:

Dort kann man jetzt in die beiden Textboxen für Pre-Build und Post-Build seine eigenen Kommandos einfügen.
Ich hab für unser Demoprojekt eine Solution erstellt, die drei Projekte speichert: Client, Web und Core. Client und Web verwenden die Core Bibliothek, die wiederrum auf Einstellung von Client oder Web zugreift.
Auf Einstellungen zugreifen
Als erstes implementieren wir den Zugriff auf die (hoffentlich) vorhandenen Konfigurationseinstellungen. In der Core Bibliothek erstellen wir uns eine Klasse, die die unterschiedlichen Einstellung aus den Konfiruationseinstellungen ausliest und zurückgibt:
public static class Settings
{
public static string Author
{
get { return ConfigurationManager.AppSettings["author"]; }
}
public static string Email
{
get { return ConfigurationManager.AppSettings["email"]; }
}
public static int Copyright
{
get { return Convert.ToInt32(ConfigurationManager.AppSettings["copyright"]); }
}
}
Über die Klasse ConfigurationManager (im Assembly System.Configuration) kann man auf die AppSettings bzw. ConnectionStrings zugreifen. In diesem Beispiel gehen wir davon aus, das wir unsere Einstellungen nur in den AppSettings speichern. ConfigurationManager greift im Hintergrund auf die Einstellungen der app|web.config zu. Da wir dieses Projekt eigentlich in dem Client bzw. Web verwenden, verwendet der ConfigurationManager indirekt die Einstellungen aus diesem Projekt. Im Web-Projekt erzeugen wir uns nun AppSettings, auf die wir später im Page-Load zugreifen:
<appSettings> <add key="author" value="Jürgen Oberngruber" /> <add key="email" value="blog@juergenoberngruber.at" /> <add key="copyright" value="2009" /> </appSettings>
Im Page_Load können wir jetzt über unsere Settings Klasse indirekt auf die Settings zugreifen:
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(Core.Settings.Author);
}
Das Ganze funktioniert natürlich auch für unseren WPF Client (dort arbeiten wir mit der app.config Datei):

Globales Konfigurationsfile
So, das Ganze ist jetzt so, wie wir es ja eigentlich gar nicht wollen. Um ein globales Konfigurationsfile zu erstellen, erzeugen wir uns einfach auf “Solution-Ebene” ein neues *config File, dass die globalen Einstellungen, die wir später in den beiden Projekten (Web und Client) verwenden, speichert. Um dieses File jetzt für die beiden Host-Projekt zugänglich zu machen, müssen wir als erstes dem web|app.config File mitteilen, dass es eine zusätzliche Konfiguration aus einem externen File nehmen soll:
<appSettings file="GlobalSettings.config"> </appSettings>
Versuchen wir jetzt das Projekt laufen zu lassen, bekommen wir einen Error, weil es die Datei nicht finden kann — was auch klar ist, weil es das File nicht in diesem Pfad gibt. Grund dafür ist, das Visual Studio das File beim kompilieren ignoriert — wir wollen aber erreichen, dass es beim Kompilieren in den bin Ordner bzw. in das richtige Verzeichnis kopiert wird. Hier kommen nun endlich unsere Post-Build Events zum Einsatz.
Über das Kommando copy versuchen wir, das GlobalSettings.config File in den richtigen Dateipfad zu kopieren. Für das Web Projekt soll es dort hin, wo die web.config liegt. Das Post-Build Kommando sieht als so aus:
copy “$(SolutionDir)GlobalSettings.config” “$(ProjectDir)GlobalSettings.config”
Visual Studio bietet uns dabei Unterstützung an, in dem es Konstanten (Macros) zur Verfügung stellt, die später beim Kompilieren aufgelöst werden:

Kompilieren wir jetzt neu, sollte die GlobalSettings.config im richtigen Ordner liegten und die file Angabe in der web.config klappt nun auch. Das gleiche funktioniert auch beim Client Projekt — dort liegt die app.config jedoch im /bin Ordner. Das tolle dabei ist jetzt auch, dass wir trotzdem in unseren einzelnen Projekten noch die “Globale Konfiguration” überschreiben bzw. neue Werte hinzufügen können (<add>, <remove> und <clear>).
Das ganze Demo Projekt kann hier herunter geladen werden: GlobalSettings_Demo (VS 2010).
Hat wer eine besser Idee —> Comments!
In diesem Sinne.
