6. Fallstudie mit PostgreSQL 9.2.1
6.1. Installation der Tools
Folgende Tools müssen installiert werden:
- SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
- ein Verwaltungstool: EMS, SQL Manager für PostgreSQL, Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].
In den folgenden Beispielen hat der Benutzer „postgres“ das Passwort „postgres“.
Starten wir PostgreSQL und anschließend das Tool [SQL Manager Lite for PostgreSQL], mit dem wir SGBD verwalten werden.
![]() |
- In [1] starten wir SGBD und PostgreSQL über die Windows-Dienste;
- bei [2] wird der Dienst gestartet;
Wir starten nun das Tool [SQL Manager Lite for MySQL], mit dem wir die Dienste SGBD und [3] verwalten werden.
![]() |
- In [4] legen wir eine neue Datenbank an;
- in [5] geben wir den Namen der Datenbank an;
![]() |
- in [5] melden wir uns als postgres / postgres an;
- In [6] geben wir einige Informationen ein;
- In [7] wird der Befehl SQL bestätigt, der nun ausgeführt wird;
![]() |
- In [8] wurde die Datenbank angelegt. Sie muss nun in [EMS Manager] registriert werden. Die Angaben sind korrekt. Wir führen [OK] aus;
- in [9] stellen wir eine Verbindung her;
- In [10] zeigt [EMS Manager] die Datenbank an, die derzeit noch leer ist. Es ist zu beachten, dass die Tabellen zu einem Schema namens „public [11]“ gehören werden.
Wir werden nun ein Projekt VS 2012 mit dieser Datenbank verbinden.
6.2. Erstellung der Datenbank anhand der Entitäten
Zunächst duplizieren wir den Projektordner „[RdvMedecins-SqlServer-01]“ in „[RdvMedecins-PostgreSQL-01]“ und „[1]“:
![]() |
- in [2], in VS 2012 löschen wir das Projekt [RdvMedecins-SqlServer-01] aus der Lösung;
![]() |
- in [3] wurde das Projekt gelöscht;
- in [4] fügen wir ein weiteres hinzu. Dieses befindet sich im Ordner [RdvMedecins-PostgreSQL-01], den wir zuvor erstellt haben;
![]() |
- In [5] heißt das geladene Projekt [RdvMedecins-SqlServer-01];
- in [6], dessen Name in [RdvMedecins-PostgreSQL-01] geändert wird;
![]() |
- In [7] wird der Lösung ein weiteres Projekt hinzugefügt. Dieses stammt aus dem Ordner [RdvMedecins-SqlServer-01] des Projekts, das wir zuvor aus der Lösung entfernt haben;
- In [8] wurde das Projekt [RdvMedecins-SqlServer-01] wieder in die Lösung aufgenommen.
Das Projekt [RdvMedecins-PostgreSQL-01] ist identisch mit dem Projekt [RdvMedecins-SqlServer-01]. Wir müssen einige Änderungen vornehmen. In [App.config] werden wir die Verbindungszeichenfolge ändern, und [DbProviderFactory] muss an jedes SGBD angepasst werden.
<!-- Verbindungszeichenfolge zur Datenbank -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- Factory-Provider -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
- Zeile 3: der Benutzer und sein Passwort;
- Zeilen 7–9: das DbProviderFactory. Zeile 8 verweist auf ein DLL und ein [Npgsql], die wir nicht haben. Man erhält sie mit NuGet [1]:
![]() |
- Bei [2] gibt man im Suchfeld das Stichwort postgresql ein;
- bei [3] wählt man das Paket [Npgsql] aus. Es handelt sich um einen Konnektor ADO.NET für PostgreSQL;
![]() |
- In [4] wurden zwei Referenzen hinzugefügt;
- in [5], in [App.config] muss die korrekte Version von DLL eingegeben werden. Diese findet man in den Eigenschaften.
In der Datei [Entites.cs] muss das Schema der zu generierenden Tabellen angepasst werden:
[Table("MEDECINS", Schema = "public")]
public class Medecin : Personne
{...}
[Table("CLIENTS", Schema = "public")]
public class Client : Personne
{...}
[Table("CRENEAUX", Schema = "public")]
public class Creneau
{...}
[Table("RVS", Schema = "public")]
public class Rv
{...}
Wir haben zuvor bei der Erstellung einer Datenbank mit dem Namen PostgreSQL gesehen, dass die Tabellen zu einem Schema namens „public“ gehörten.
Wir konfigurieren die Ausführung des Projekts:
![]() |
- In [1] geben wir der zu generierenden Assembly einen anderen Namen;
- in [2] legen wir außerdem einen anderen Standard-Namespace fest;
- in [3] legen wir das auszuführende Programm fest.
Zu diesem Zeitpunkt liegen keine Kompilierungsfehler vor. Führen wir das Programm [CreateDB_01] aus. Dabei tritt folgende Ausnahme auf:
Wir erinnern uns, dass wir denselben Fehler bereits bei MySQL und Oracle hatten. Dies hängt mit dem Feldtyp Timestamp der Entitäten zusammen. Wir nehmen dieselbe Änderung vor wie bei Oracle. In den Entitäten ersetzen wir die drei Zeilen
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
durch die folgenden:
[ConcurrencyCheck]
[Column("VERSIONING")]
public int? Versioning { get; set; }
Wir ändern den Typ der Spalte von byte[] in int?. In SGBD verwenden wir gespeicherte Prozeduren, um diese Ganzzahl jedes Mal um eins zu erhöhen, wenn eine Zeile eingefügt oder geändert wird.
Wir nehmen die oben beschriebene Änderung an allen vier Entitäten vor und führen die Anwendung anschließend erneut aus. Daraufhin erhalten wir folgende Fehlermeldung:
Zeile 1 weist darauf hin, dass der Konnektor ADO.NET von PostgreSQL nicht in der Lage ist, die vorhandene Datenbank zu löschen. Genau wie bei Oracle. Wir müssen daher die Datenbank [RDVMEDECINS-EF] manuell mit dem Tool [EMS Manager for PostgreSQL] anlegen. Wir beschreiben nicht alle Schritte, sondern nur die wichtigsten.
Die Datenbank PostgreSQL wird wie folgt aussehen:
Die Tabellen
![]() |
- in [1] und ID ist der Primärschlüssel vom Typ serial. Dieser Typ PostgreSQL ist eine Ganzzahl, die automatisch vom SGBD generiert wird.
![]() |
![]() |
![]() |
![]() |
Die verschiedenen Tabellen verfügen über dieselben Primär- und Fremdschlüssel wie die entsprechenden Tabellen in den vorherigen Beispielen. Die Fremdschlüssel haben die Attribute ON, DELETE und CASCADE.
Die Sequenzen
Wie bei Oracle haben wir hier Sequenzen angelegt. Dabei handelt es sich um Generatoren für fortlaufende Zahlen. Es gibt 5 davon: [1].
![]() |
- In [2] sehen wir die Eigenschaften der Sequenz [CLIENTS_ID_SEQ]. Sie generiert aufeinanderfolgende Zahlen im Abstand von 1, beginnend bei 1 bis zu einem sehr großen Wert.
Alle Sequenzen sind nach dem gleichen Muster aufgebaut.
- [CLIENTS_ID_seq] wird zur Generierung des Primärschlüssels der Tabelle [CLIENTS] verwendet;
- [MEDECINS_ID_seq] wird zur Generierung des Primärschlüssels der Tabelle [MEDECINS] verwendet;
- [CRENEAUX_ID_seq] wird zur Generierung des Primärschlüssels der Tabelle [CRENEAUX] verwendet;
- [RVS_ID_seq] wird zur Generierung des Primärschlüssels der Tabelle [RVS] verwendet;
- [sequence_versions] wird zur Generierung der Werte der Spalten [VERSIONING] aller Tabellen verwendet.
Trigger
Ein Trigger ist eine Prozedur, die von SGBD vor oder nach einem Ereignis (Einfügen, Ändern, Löschen) in einer Tabelle ausgeführt wird. Wir haben 4 davon: [1]:
![]() |
Sehen wir uns den Code DDL des Triggers [CLIENTS_tr] an, der die Spalte [VERSIONING] der Tabelle [CLIENTS] befüllt:
- Zeilen 1–3: vor jeder Operation INSERT oder UPDATE an der Tabelle [CLIENTS];
- Zeile 4: Die Prozedur [public.trigger_versions()] wird ausgeführt.
Die Prozedur [public.trigger_versions()] lautet wie folgt:
- Zeile 2: NEW steht für die Zeile, die eingefügt oder geändert werden soll. NEW. „VERSIONING“ ist die Spalte [VERSIONING] dieser Zeile. Ihr wird der folgende Wert des Zahlengenerators „sequence_versions“ zugewiesen. Somit ändert sich die Spalte ["VERSIONING"] bei jeder Ausführung von INSERT / UPDATE auf der Tabelle [CLIENTS].
Die Trigger [MEDECINS_tr, CRENEAUX_tr, RVS_tr] funktionieren analog. Die vier Spalten ["VERSIONING"] beziehen ihre Werte aus derselben Sequenz.
Das Skript zur Erstellung der Datenbanktabellen PostgreSQL und [RDVMEDECINS-EF] wurde im Ordner [RdvMedecins / databases / postgreSQL] abgelegt. Der Leser kann es laden und ausführen, um seine Tabellen zu erstellen.
Anschließend können die verschiedenen Programme des Projekts ausgeführt werden. Sie liefern dieselben Ergebnisse wie mit dem Server SQL, mit Ausnahme des Programms [ModifyDetachedEntities], das aus demselben Grund abstürzt, aus dem es auch bei Oracle abgestürzt war. Das Problem lässt sich auf dieselbe Weise beheben. Es reicht aus, das Programm [ModifyDetachedEntities] aus dem Projekt [RdvMedecins-Oracle-01] in das Projekt [RdvMedecins-PostgreSQL-01] zu kopieren.
Das Programm [LazyEagerLoading] stürzt mit folgender Ausnahme ab:
Der fehlerhafte Code lautet wie folgt:
using (var context = new RdvMedecinsContext())
{
// Zeitfenster Nr. 0
creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
Console.WriteLine(creneau.ShortIdentity());
}
Zeile Nr. 1 der Ausnahme: Der gemeldete Fehler lässt auf eine Verknüpfung schließen, da LEFT ein Schlüsselwort der Verknüpfung ist. Da in Zeile 4 des obigen Codes das sofortige Laden der Abhängigkeit [Medecin] von einer Entität [Creneau] angefordert wird, hat EF eine Verknüpfung zwischen den Tabellen [CRENEAUX] und [MEDECINS] hergestellt. Es scheint jedoch, dass der Konnektor ADO.NET einen fehlerhaften Befehl SQL generiert hat. Wir schreiben den Code wie folgt um:
using (var context = new RdvMedecinsContext())
{
// Termin Nr. 0
creneau = context.Creneaux.Find(idCreneau);
Console.WriteLine(creneau.ShortIdentity());
// Das Laden des zugehörigen Arztes wird erzwungen
// Das ist möglich, da wir uns noch in einem offenen Kontext befinden
Medecin medecin = creneau.Medecin;
}
- Zeile 4: Wir suchen den Zeitrahmen ohne Verknüpfung;
- Zeile 8: Wir holen die fehlende Abhängigkeit ab.
Es funktioniert. Erneut stellen wir fest, dass die Änderung an SGBD Auswirkungen auf den Code hat. Tatsächlich ist hier nicht SGBD das Problem, sondern dessen Konnektor ADO.NET.
6.3. Mehrschichtige Architektur auf Basis von EF 5
Wir kehren zu unserer in Absatz 2 beschriebenen Fallstudie zurück.
![]() |
Wir beginnen mit der Erstellung der Datenzugriffsebene [DAO]. Dazu duplizieren wir das Konsolenprojekt VS 2012 [RdvMedecins-SqlServer-02] in [RdvMedecins-PostgreSQL-02] [1]:
![]() |
- in [2], das Projekt [RdvMedecins-SqlServer-02] wird gelöscht;
![]() |
- In [3] wird ein bestehendes Projekt zur Lösung hinzugefügt. Es wird aus dem soeben erstellten Ordner [RdvMedecins-PostgreSQL-02] übernommen;
- In [4] trägt das neue Projekt den Namen des gelöschten Projekts. Wir werden seinen Namen ändern;
![]() |
- In [5] haben wir den Namen des Projekts geändert;
- in [6]: Wir ändern einige seiner Eigenschaften, wie hier den Namen der Assembly;
- in [7]; der Ordner [Models] wird gelöscht und durch den Ordner [Models] aus dem Projekt [RdvMedecins-PostgreSQL-01] ersetzt. Tatsächlich nutzen beide Projekte dieselben Vorlagen.
![]() |
- in [8], die aktuellen Projektreferenzen;
- in [9] wurde der Konnektor ADO.NET aus PostgreSQL mit dem Tool NuGet hinzugefügt.
In der Datei [App.config] werden die Informationen der Datenbank SQL Server durch die der Datenbank PostgreSQL ersetzt. Diese finden sich in der Datei [App.config] des Projekts [RdvMedecins-PostgreSQL-01]:
<!-- Verbindungskette zur Datenbank -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- Der Factory-Provider -->
<system.data>
<DbProviderFactories>
<add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql Server" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.11.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
Auch die von Spring verwalteten Objekte ändern sich. Derzeit haben wir:
<!-- Spring-Konfiguration -->
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
</objects>
</spring>
Zeile 7 verweist auf die Assembly des Projekts [RdvMedecins-SqlServer-02]. Die Assembly lautet nun [RdvMedecins-PostgreSQL-02].
Nachdem dies erledigt ist, können wir den Test der Schicht [DAO] ausführen. Zuvor muss jedoch die Datenbank gefüllt werden (Programm [Fill] aus dem Projekt [RdvMedecins-PostgreSQL-01]). Das Testprogramm stürzt mit folgender Ausnahme ab:
Zeile 13: Die Meldung weist darauf hin, dass der Fehler in der Methode [GetCreneauxMedecin] der Schicht [DAO] aufgetreten ist. Diese lautet wie folgt:
// Liste der Terminfenster eines bestimmten Arztes
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// Liste der Termine
try
{
// Öffnen des Persistenzkontexts
using (var context = new RdvMedecinsContext())
{
// Abruf des Arztes mit seinen Terminfenstern
Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
// Die Liste der Termine des Arztes wird zurückgegeben
return medecin.Creneaux.ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
In Zeile 11 erkennt man das Schlüsselwort Include, das bereits ein vorheriges Programm zum Absturz gebracht hat. Der vorherige Code kann durch den folgenden ersetzt werden:
// Liste der Zeitfenster eines bestimmten Arztes
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// Liste der Zeitfenster
try
{
// Persistenzkontext öffnen
using (var context = new RdvMedecinsContext())
{
// Die Liste der Termine des Arztes wird zurückgegeben
return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
Der neue Code wirkt sogar schlüssiger als der alte. Jedenfalls läuft das Testprogramm dieses Mal erfolgreich ab.
Wir erstellen die Datei „DLL“ des Projekts, wie es bereits für das Projekt „[RdvMedecins-SqlServer-02]“ geschehen ist, und sammeln allealle DLL des Projekts in einem Ordner [lib] zusammen, der in [RdvMedecins-PostgreSQL-02] angelegt wurde. Dies sind die Referenzdateien für das Webprojekt [RdvMedecins-PostgreSQL-03], das als Nächstes folgt.
![]() |
Wir sind nun bereit, die Ebene [ASP.NET] unserer Anwendung zu erstellen:
![]() |
Wir gehen vom Projekt [RdvMedecins-SqlServer-03] aus. Wir duplizieren den Ordner dieses Projekts in [RdvMedecins-PostgreSQL-03] und [1]:
![]() |
- in [2], mit VS 2012 Express für das Web öffnen wir die Lösung aus dem Ordner [RdvMedecins-PostgreSQL-03];
- in [3] ändern wir sowohl den Namen der Lösung als auch den Namen des Projekts;
![]() |
- In [4] werden die aktuellen Projektverweise;
- in [5] löschen wir sie;
- in [6], um sie durch Verweise auf die Datei DLL zu ersetzen, die wir gerade in einem Ordner [lib] des Projekts [RdvMedecins-PostgreSQL-02] gespeichert haben.
Nun müssen wir nur noch die Datei [Web.config] ändern. Wir ersetzen ihren aktuellen Inhalt durch den Inhalt der Datei [App.config] aus dem Projekt [RdvMedecins-PostgreSQL-02]. Anschließend führen wir das Webprojekt aus. Es funktioniert. Vergessen Sie nicht, die Datenbank zu füllen, bevor Sie die Webanwendung ausführen.


























