In diesem Post möchte ich ein paar Praxistipps zur grundlegenden Zusammenarbeit in ASP.NET-Projekten geben. Dabei beschränke ich mich auf die Grundvoraussetzungen, die erfüllt sein müssen, wenn mehr als eine Person an einem Projekt arbeitet. Das man das alles noch weiterspinnen und durch den Einsatz von Visual Studio Team System beispielsweise auf ein anderes Level heben kann, versteht sich von selbst.
Grundvoraussetzung für die Arbeit im Team ist selbstverständlich eine Quellcodeverwaltung, sie stellt den Dreh- und Angelpunkt dar. Da ich VSS-geschädigt bin, setze ich wo immer ich die Wahl habe, seit anderthalb Jahren auf Subversion. Tipps zur Einrichtunge usw. gibt es hier.
Hat man diese eingerichet, stellen sich in der Praxis die folgenden Probleme in den Arbeitsweg:
- Jeder Entwickler hat einen anderen Rechner, womöglich eine andere Windows-Version, andere Partitionen usw.
- Jeder Entwickler hat seine eigene lokale Datenbank, womöglich unter verschiedenen Instanzen.
Für das Erste Problem gilt:
- Verwenden von relativen Pfaden, sowohl was die Applikations-Konfiguration angeht, als auch z.B. zu Bildern innerhalb des Webs. Hier z.B. immer "~/Bilder/" verwenden, anstatt "/Bilder". Dann funktioniert es sowohl im Root eines Webservers, als auch mit einer Applikation.
- Verwenden des VisualStudio-Webservers oder Nutzung des lokalen IIS mit gleich lautenden Applikations-Bezeichnungen. So kann sich Entwickler 1 unter localhost/WebX das Web unter D:\ einrichten, Entwickler 2 kann das Web auf seiner Wechselfestplatte unter Z:\ liegen haben, und erreicht es trotzdem über localhost/WebX.
Falls dennoch einmal feste physische Pfade unabdingbar sind, z.B. weil man auf Dateien/Ordner außerhalb des Webroots zugreifen muss, auf die man mit Server.MapPath() nicht mehr kommt, dann bietet es sich an, diese Pfade in der Web.config zu speichern, und mit einer lokalen, nicht in der Quellcodeverwaltung eingecheckten User-bezogenen Config zu überschreiben.
Das funktioniert wie folgt:
Man definiert in seiner Web.config in der Sektion appSettings seinen Eintrag:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="Pfad" value="Z:\" />
</appSettings>
<connectionStrings/>
<system.web>
<compilation debug="false" />
<authentication mode="Windows" />
</system.web>
</configuration>
Nun fügt man <appSettings /> noch das Attribut file hinzu:
<appSettings file="Web.User.config">
<add key="Pfad" value="Z:\" />
</appSettings>
Dieses File ist nichts weiter als eine "Custom Web.config" für den Entwickler, etwa sowas wie das .suo-File, was von Visual Studio für jeden Benutzer erstellt wird, und in dem benutzerspezifische Einstellungen gespeichert werden (etwa die Namen der geöffneten Dateien, deswegen öffnet VS beim Öffnen der Solution immer die richtigen Files, die beim letzten Mal noch offen waren). Das File selbst beinhaltet dann den benutzerdefinierten Inhalt der AppSettings:
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="Pfad" value="Z:\" />
</appSettings>
Wichtig und interessant ist hier aber die Vorgehensweise, wie dieses File behandelt wird: Ist es vorhanden, wird der Eintrag aus diesem File genommen, und der in der eigentlichen Web.config überschrieben. Ist die Web.User.config nicht vorhanden, oder wird kein entsprechender Eintrag mit dem Key "Pfad" gefunden, wird der Eintrag aus der herkömmlichen Web.config verwendet.
Das heißt in der Praxis: Alle Applikations-Einstellungen werden in der Web.Config gespeichert, dazu wird noch auf Web.User.config verlinkt. Nun kann sich jeder Entwickler seine eigene Web.User.config anlegen und die AppSettings anpassen, wie er sie zum lokalen Betrieb des Webs benötigt. Auf dem Server oder beim Kunden wird die Datei Web.User.config gar nicht erst angelegt oder gelöscht - womit dann wieder die Einstellungen aus Web.config greifen.
Genial? Ja! Aber ...
Die Entwickler von ASP.NET 2.0 haben leider nicht sehr weit gedacht, oder sich nicht abgesprochen. Vielleicht gab es auch ein Kompetenzgerangel, ich weiß es nicht. Mit ASP.NET 2.0 hat nämlich für die meisten Web.config-Sektionen ein neues Attribut Einzug gehalten: configSource.
ConfigSource erlaubt es ebenfalls, eine externe Konfigurationsdatei zu bestimmen, aus der die Konfiguration für die Sektion bezogen werden soll:
<connectionStrings configSource="">
<add connectionString="" name=""/>
</connectionStrings>
Aber ich schrieb ja von einem "Aber". Und das lautet: Definiert man eine externe "Konfigurationsquelle", so darf in der Web.config in dieser Sektion selbst nichts mehr stehen. Auf Deutsch heißt das: das Vorgehen wie bei den AppSettings mit dem File-Attribut ist hier nicht möglich. Man kann zwar die Konfiguration für einzelne Bereiche auslagern, aber deren Verwendung/Erkennung ist weitaus dümmer. Damit ist das leider in der Praxis so gut wie wertlos, und die ganze Funktionalität überflüssig. Wer es nicht glaub, soll es mal ausprobieren. Einfach mal eine ConfigSource angeben und den Inhalt in z.B. ConnectionStrings in der Web.config stehen lassen ... es wird krachen.
Was mich zu Problem 2 bringt.
Was tun, wenn die am Projekt beteiligten Entwickler aus diversesten Gründen unterschiedliche lokale Datenbankkonfigurationen nutzen? Entwickler 1 hat seinen SQL-Server z.B. als Hauptinstanz unter (local) laufen, Entwickler 2 unter (local)\SQL2005 instanziert.
Prinzipiell bieten sich jetzt folgende Möglichkeiten an:
- Da ConfigSource Nonsense ist, kann man die Connectionstrings einfach in die AppSettings packen. Damit kann jeder Entwickler seinen eigenen ConnectionString in seiner Web.user.config anpassen. Das sollte in 95% der Fälle funktionieren - zumindest immer dort, wo man sich auf ein festes Datenbanksystem geeinigt hat, und die anderen Vorteile/Eigenschaften der ConnectionStrings-Sektion nicht nutzt.
- Man kapselt das Auslesen des ConnectionStrings in einer Helper-Klasse in eine eigene Methode, und erledigt das intelligente Unterscheiden ob der globale oder der User-bezogene ConnectionString verwendet werden soll, hier. Das kann man machen in dem man z.B. schaut, ob eine Web.User.config vorhanden ist, auf die man dann zugreift ohne in der Web.config ConfigSource definiert zu haben.
- Man einigt sich unter allen Entwicklern auf einen Alias für die Datenbank-Server-Instanz.
Letzteres ist vielleicht am bequemsten und erfordert am wenigsten Arbeit (wenn man es einmal drauf hat), Variante 2 ist sicher am flexibelsten.
Wie man Aliase für den SQL-Server vergibt, beschreibe ich demnächst. Hiermit wären jetzt jedenfalls die grundlegenden Probleme in der Zusammenarbeit an einem Projekt abgehandelt, die mir in den letzten Jahren untergekommen sind.
Ein ganz großes Fass kann man zudem noch aufmachen, wenn es darum geht, wie man die Datenbank im Repository hält, wie man CREATE- und ALTER-Scripts aufbaut, verwaltet und benutzt usw. Hierzu werde ich demnächst auch noch etwas schreiben, das muss ich aber selbst erstmal in der Praxis erproben ;-).