Von der Datenbank bis zur Oberfläche mit .NET, Teil 1: Datenzugriff und Logik

Know-how  –  24 Kommentare

.NET rühmt sich der durchgängigen Programmiersprachen und -techniken vom Server bis zum Client. In vielen Beispielen sieht man aber oft nur einzelne umgesetzt. In einer fünfteiligen Artikelserie zeigt heise Developer, wie in einem mehrschichtigen Projekt verschiedene .NET-Techniken unterschiedlich zusammenarbeiten können. Der Teil 1 beginnt mit dem Datenzugriff und der Geschäftslogik.

Der Fokus des Fallbeispiels soll auf dem Zusammenspiel der Techniken liegen, nicht auf dem Geschäftsprozess. Letzterer ist daher bewusst überschaubar gewählt: Aufgabe ist es, eine Buchungsmaske für Flüge aus der Sicht eines Call-Center-Mitarbeiters zu implementieren. Dabei wählt der Anwender einen Passagier sowie einen Flug und löst die Buchung aus. Die Auswahl kann jeweils über die Eingabe einer ID erfolgen oder über eine Suche nach Namen (bei Passagieren) beziehungsweise Flugstrecke (bei Flügen). Preisunterschiede, Rückflüge, Mitreisende, Vielfliegerkarten und viele andere alltägliche Fälle des Flugverkehrs bleiben unberücksichtigt. Auch Authentifizierung soll kein Thema sein. Angelehnt ist das Tutorial an die Beispielanwendung von World Wide Wings, einer fiktiven Fluggesellschaft, die alle Anwendung mit .NET erstellt.

Um das Beispiel mitzuprogrammieren, benötigt der Entwickler Grundkenntnisse in der C#-Syntax und der Handhabung von Visual Studio 2010. Eingesetzt werden sollte Visual Studio 2010 Professional oder höher. Damit ist auch .NET 4.0 auf dem Entwicklungssystem vorhanden. Grundsätzlich könnte man mit den kostenfreien Express-Versionen ebenfalls zum Ziel kommen, das Handhaben der Projekte wäre aber aufwendiger und würde mehr Komplexität in das Beispiel bringen. Wer nicht mitprogrammieren will, kann sich das Ergebnis jedes einzelnen Teils hier herunterladen.

Das Tutorial verwendet die englische Version von Visual Studio, weil es in der deutschen Ausgabe einen Fehler gibt, durch den das Tutorial-Beispiel nicht ohne viel manuelle Arbeit zum Laufen gebracht werden kann. Anzumerken sei zudem, dass bei Beobachtung der Entwicklerarbeitsplätze in Deutschland dort zunehmend mehr englische Versionen anzutreffen sind.

Für das Beispiel soll angenommen werden, dass die Anwendung auf der "grünen Wiese" beginnt. Es gibt also weder irgendwelche Logik noch Datenbankstrukturen. Dieser Fall ist ein gutes Szenario, um sich vom relationalen Datenbankdesign zu lösen und das Objektmodell in den Mittelpunkt des Designs zu stellen. Statt also "klassisch" als Erstes eine Datenbank zu gestalten und dann eine Datenzugriffsschicht dafür zu programmieren, entsteht ein Objektmodell, aus dem der Entwickler eine Datenbank generiert. Der Ansatz heißt "Model First" und wird vom ADO.NET Entity Framework (EF), dem objektrelationalen Mapper (ORM) von .NET, zur Verfügung gestellt.

Das erste Projekt soll die Heimat für die Geschäftsobjekte sein. Nach dem Start von Visual Studio ist es unter File | New Project anzulegen. Dann wählt der Entwickler als Projekttyp Visual C# | Class Library aus und gibt als Namen "WWWings_GO" an. Als "Solution Name" für die Projektmappe wählt er "WWWings". Bei "Location" hat er einen Pfad anzugeben, der keine Leerzeichen enthalten darf (siehe Abb. 1). Wenn man den Team Foundation Server (TFS) oder eine andere, in Visual Studio integrierte Quellcodeverwaltung verwendet, kann man auch "Add to Source Control" wählen und einen Pfad in der Quellcodeverwaltung für das Projekt festlegen.

Projekt mit Projektmappe anlegen (Abb. 1)

Danach löscht der Entwickler im angelegten Projekt die Datei Class1 und fügt statt ihr mit Add New Item ein Element des Typs "ADO.NET Entity Data Model" mit Namen WWWingsModell.edmx hinzu. Im folgenden Dialog wählt er "Empty Model", da es ja keine Datenbank gibt. Nun gilt es, mit den drei Elementen der Werkzeugleiste (Entity, Association und Inheritance) das in Abbildung 2 dargestellte Modell zusammenzuklicken. Das geht recht intuitiv, hier sollen nur einige Hinweise gegeben werden: Die vier Entitäten "Person", "Passagier", "Pilot" und "Flug" legt der Entwickler mit dem Element "Entity" aus der Werkzeugleiste an. "Pilot" und "Passagier" erben von "Person", das erreicht man mit dem Element "Inheritance", das man von der erbenden auf die vererbende Klasse zieht. Die automatisch erzeugten "Id"-Elemente in den abgeleiteten Klassen muss der Entwickler händisch löschen. Die weiteren Attribute der Klasse sind vom Typ "string", außer den Attributen mit "Datum" im Namen. Diese sind im "Properties Window" auf "DateTime" zu setzen.

Das zu erzeugende Objektmodell (Abb. 2)

Bei "Geburtsdatum" sollte man außerdem "Nullable" auf "false" stellen; diese Angabe ist optional. "Plaetze" und "FreiePlaetze" müssen den Typ "Int16" zugewiesen bekommen. Bei "Flug.ID" setzt man zudem "StoreGenerationPattern" auf "None". Damit werden die Primärschlüssel für Flüge nicht automatisch vergeben, sondern man kann diese im Sinne von Nummernkreisen selbst eintragen. Die Personen-IDs soll hingegen die Datenbank vergeben (Autowerte). Daher belässt man dort den Eintrag auf "Identity".

Zwischen "Passagier" und "Flug" gibt es eine n:m-Beziehung (many to many), zwischen "Pilot" und "Flug" eine 1:n-Beziehung (one to many) – die Rationalisierung hat also auch im Cockpit zugeschlagen, indem der Co-Pilot abgeschafft wurde (wie von Ryanair vorgeschlagen). Die Beziehungen legt man besser nicht über das Element "Association" aus der Werkzeugleiste an, sondern indem man Add | Association im Kontextmenü in der Designeroberfläche wählt. Bei der 1:n-Beziehung sollte man "Add Foreign Key properties" wählen, damit man später Beziehungen zwischen "Flug" und "Pilot" auch auf Schlüsselebene herstellen kann und dafür nicht notwendigerweise den "Pilot" komplett laden muss. Die "PilotId" in Flüge muss man daher nicht eingeben; durch das Erstellen der Assoziation zu "Pilot" wird dieses Element automatisch erzeugt.

Anlegen einer 1:n-Assoziation mit zusätzlicher Fremdschlüsselbeziehung (Abb. 3)

Durch das Zusammenklicken des Objektmodells entstehen fünf .NET-Klassen in einer Datei WWWingsModell.Designer.cs, die man aber nur sieht, wenn man im Solution Explorer die Option
"Show All Files" gewählt hat: eine Klasse für jede Entität (Entitätsklassen) und eine sogenannte Kontextklasse, die den Zugang zu den Instanzen der Entitätsklassen bietet. In der deutschen Version von Visual Studio kommt es hier zum oben erwähnten Fehler. Unter der hinterlegten URL ist beschrieben, wie man ihn mit manuellen Eingriffen in die von Microsoft gelieferten Codegenerierungsvorlagen beseitigen kann.

Nun sind noch einige Erweiterungen für die Entitätsklasse manuell umzusetzen. Das geht einfach, weil die generierten Entitätsklassen alle als "partial" deklariert sind. Dafür muss man im Projekt eine Klassendatei
EntitaetsklassenErweiterungen.cs anlegen mit dem Inhalt aus Listing 1 (siehe oben rechts). Dadurch erhalten "Person" (und die davon abgeleiteten Klassen) sowie "Flug" jeweils ein zusätzliches Attribut, und die Ausgabe der Objekte wird erleichtert, indem ToString() aus der allgemeinen Basisklasse System.Object überschrieben wird. Ohne das würde ToString() immer nur den Klassennamen liefern. Bei den Erweiterungen ist zu bedenken, dass man hier keine zu persistierenden Attribute schaffen kann. Diese lassen sich nur im Modell anlegen.