6. Studium przypadku z wykorzystaniem pliku PostgreSQL 9.2.1
6.1. Instalacja narzędzi
Należy zainstalować następujące narzędzia:
- SGBD: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
- narzędzie administracyjne: EMS, SQL Manager for PostgreSQL, Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].
W poniższych przykładach użytkownik postgres ma hasło postgres.
Uruchommy PostgreSQL, a następnie narzędzie [SQL Manager Lite for PostgreSQL], za pomocą którego będziemy zarządzać SGBD.
![]() |
- w [1] uruchamiamy SGBD i PostgreSQL z poziomu usług systemu Windows;
- w przypadku [2] usługa zostaje uruchomiona;
Teraz uruchamiamy narzędzie [SQL Manager Lite for MySQL], za pomocą którego będziemy zarządzać SGBD i [3].
![]() |
- w [4] tworzymy nową bazę;
- w [5] podajemy nazwę bazy;
![]() |
- w pliku [5] logujemy się jako postgres / postgres;
- w [6] podajemy kilka informacji;
- w [7] zatwierdza się zlecenie SQL, które zostanie wykonane;
![]() |
- w [8] baza została utworzona. Teraz należy ją zarejestrować w [EMS Manager]. Informacje są poprawne. Wykonujemy [OK];
- w [9] logujemy się do niej;
- w [10], [EMS Manager] wyświetla bazę danych, która na razie jest pusta. Należy zauważyć, że tabele będą należeć do schematu o nazwie public [11].
Teraz podłączymy do tej bazy projekt VS 2012.
6.2. Tworzenie bazy danych na podstawie encji
Zaczynamy od skopiowania folderu projektu [RdvMedecins-SqlServer-01] do [RdvMedecins-PostgreSQL-01] i [1]:
![]() |
- do [2], w VS 2012 usuwamy projekt [RdvMedecins-SqlServer-01] z rozwiązania;
![]() |
- w [3] projekt został usunięty;
- w [4] dodajemy kolejny projekt. Znajduje się on w folderze [RdvMedecins-PostgreSQL-01], który utworzyliśmy wcześniej;
![]() |
- w pliku [5] załadowany projekt nosi nazwę [RdvMedecins-SqlServer-01];
- w [6] zmieniamy nazwę na [RdvMedecins-PostgreSQL-01];
![]() |
- w [7] dodajemy kolejny projekt do rozwiązania. Znajduje się on w folderze [RdvMedecins-SqlServer-01] projektu, który wcześniej usunęliśmy z rozwiązania;
- w [8] projekt [RdvMedecins-SqlServer-01] został ponownie dodany do rozwiązania.
Projekt [RdvMedecins-PostgreSQL-01] jest identyczny z projektem [RdvMedecins-SqlServer-01]. Musimy wprowadzić kilka zmian. W projekcie [App.config] zmodyfikujemy ciąg połączenia, a projekt [DbProviderFactory] należy dostosować do każdego projektu SGBD.
<!-- ciąg połączenia z bazą danych -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- dostawca fabryki -->
<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>
- wiersz 3: nazwa użytkownika i hasło;
- wiersze 7–9: plik DbProviderFactory. Wiersz 8 odwołuje się do pliku DLL [Npgsql], którego nie posiadamy. Można go uzyskać za pomocą NuGet [1]:
![]() |
- w [2], w polu wyszukiwania wpisujemy słowo kluczowe postgresql;
- w przypadku [3] należy wybrać pakiet [Npgsql]. Jest to łącznik ADO.NET dla PostgreSQL;
![]() |
- w [4] dodano dwa numery referencyjne;
- w [5], w [App.config] należy podać prawidłową wersję DLL. Można ją znaleźć w jego właściwościach.
W pliku [Entites.cs] należy dostosować schemat tabel, które zostaną wygenerowane:
[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
{...}
Jak widzieliśmy wcześniej podczas tworzenia bazy danych PostgreSQL, tabele należały do schematu o nazwie „public”.
Konfigurujemy uruchomienie projektu:
![]() |
- w [1] nadajemy inną nazwę generowanemu zestawowi;
- w [2], a także inną domyślną przestrzeń nazw;
- w [3] wskazujemy program do uruchomienia.
Na tym etapie nie ma żadnych błędów kompilacji. Uruchommy program [CreateDB_01]. Otrzymujemy następujący wyjątek:
Pamiętamy, że ten sam błąd wystąpił w przypadku programu MySQL i Oracle. Jest to związane z typem pola Timestamp w encjach. Wprowadzamy tę samą modyfikację, co w przypadku Oracle. W encjach zastępujemy trzy wiersze
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
następującymi:
[ConcurrencyCheck]
[Column("VERSIONING")]
public int? Versioning { get; set; }
Zmieniamy typ kolumny z byte[] na int?. W SGBD wykorzystamy procedury przechowywane, aby zwiększać tę liczbę całkowitą o jeden za każdym razem, gdy wstawiany lub modyfikowany jest wiersz.
Wprowadzamy powyższą zmianę dla wszystkich czterech encji, a następnie ponownie uruchamiamy aplikację. Otrzymujemy wówczas następujący błąd:
Wiersz 1 wskazuje, że łącznik ADO.NET z PostgreSQL nie jest w stanie usunąć istniejącej bazy danych. Dokładnie tak samo jak w przypadku Oracle. Musimy zatem ręcznie utworzyć bazę danych [RDVMEDECINS-EF] za pomocą narzędzia [EMS Manager for PostgreSQL]. Nie opisujemy wszystkich kroków, a jedynie te najważniejsze.
Baza danych PostgreSQL będzie miała następujący wygląd:
Tabele
![]() |
- w [1], ID jest kluczem głównym typu serial. Ten typ PostgreSQL jest liczbą całkowitą generowaną automatycznie przez SGBD.
![]() |
![]() |
![]() |
![]() |
Poszczególne tabele posiadają klucze główne i obce, które te same tabele miały w poprzednich przykładach. Klucze obce mają atrybuty ON, DELETE oraz CASCADE.
Sekwencje
Podobnie jak w przypadku Oracle, utworzyliśmy tutaj sekwencje. Są to generatory kolejnych liczb. Jest ich 5: [1].
![]() |
- W [2] widzimy właściwości sekwencji [CLIENTS_ID_SEQ]. Generuje ona kolejne liczby o różnicy 1, począwszy od 1 aż do bardzo dużej wartości.
Wszystkie sekwencje są zbudowane według tego samego wzorca.
- Sekwencja [CLIENTS_ID_seq] zostanie wykorzystana do wygenerowania klucza głównego tabeli [CLIENTS];
- [MEDECINS_ID_seq] zostanie wykorzystana do wygenerowania klucza głównego tabeli [MEDECINS];
- [CRENEAUX_ID_seq] zostanie wykorzystany do wygenerowania klucza głównego tabeli [CRENEAUX];
- [RVS_ID_seq] zostanie wykorzystany do wygenerowania klucza głównego tabeli [RVS];
- [sequence_versions] zostanie wykorzystana do wygenerowania wartości kolumn [VERSIONING] we wszystkich tabelach.
Wyzwalacze
Wyzwalacz to procedura wykonywana przez SGBD przed lub po zdarzeniu (wstawienie, modyfikacja, usunięcie) w tabeli. Mamy 4 wyzwalacze [1]:
![]() |
Przyjrzyjmy się kodowi DDL wyzwalacza [CLIENTS_tr], który zasilają kolumnę [VERSIONING] w tabeli [CLIENTS]:
- wiersze 1–3: przed każdą operacją INSERT lub UPDATE na tabeli [CLIENTS];
- wiersz 4: wykonywana jest procedura [public.trigger_versions()].
Procedura [public.trigger_versions()] wygląda następująco:
- wiersz 2: NEW oznacza wiersz, który ma zostać wstawiony lub zmodyfikowany. NEW. „VERSIONING” to kolumna [VERSIONING] tego wiersza. Przypisuje się jej następującą wartość z generatora liczb: „sequence_versions”. W ten sposób kolumna ["VERSIONING"] zmienia się przy każdym operacji INSERT / UPDATE wykonanej na tabeli [CLIENTS].
Podobnie działają wyzwalacze [MEDECINS_tr, CRENEAUX_tr, RVS_tr]. Cztery kolumny ["VERSIONING"] pobierają swoje wartości z tej samej sekwencji.
Skrypt generujący tabele bazy danych PostgreSQL i [RDVMEDECINS-EF] został umieszczony w folderze [RdvMedecins / databases / postgreSQL]. Użytkownik może go załadować i uruchomić w celu utworzenia tabel.
Po wykonaniu tej czynności można uruchomić poszczególne programy projektu. Dają one takie same wyniki jak w przypadku serwera SQL, z wyjątkiem programu [ModifyDetachedEntities], który ulega awarii z tego samego powodu, co w przypadku Oracle. Problem ten rozwiązuje się w ten sam sposób. Wystarczy skopiować program [ModifyDetachedEntities] z projektu [RdvMedecins-Oracle-01] do projektu [RdvMedecins-PostgreSQL-01].
Program [LazyEagerLoading] ulega awarii z następującym wyjątkiem:
Błędny kod to:
using (var context = new RdvMedecinsContext())
{
// slot nr 0
creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
Console.WriteLine(creneau.ShortIdentity());
}
Wiersz nr 1 wyjątku – zgłoszony błąd sugeruje, że chodzi o połączenie, ponieważ LEFT jest słowem kluczowym tego połączenia. Ponieważ w wierszu 4 powyższego kodu żądane jest natychmiastowe załadowanie zależności [Medecin] od encji [Creneau], EF wykonało połączenie tabel [CRENEAUX] i [MEDECINS]. Wydaje się jednak, że łącznik ADO.NET wygenerował nieprawidłowe polecenie SQL. Przepisujemy kod w następujący sposób:
using (var context = new RdvMedecinsContext())
{
// slot nr 0
creneau = context.Creneaux.Find(idCreneau);
Console.WriteLine(creneau.ShortIdentity());
// wymuszamy załadowanie powiązanego lekarza
// jest to możliwe, ponieważ nadal znajdujemy się w otwartym kontekście
Medecin medecin = creneau.Medecin;
}
- wiersz 4: wyszukujemy przedział bez połączeń;
- wiersz 8: pobieramy brakującą zależność.
Działa. Ponownie zauważamy, że zmiana w SGBD ma wpływ na kod. W rzeczywistości to nie SGBD jest tutaj przyczyną, ale jego łącznik ADO.NET.
6.3. Architektura wielowarstwowa oparta na EF 5
Wracamy do naszego studium przypadku opisanego w akapicie 2.
![]() |
Zaczniemy od zbudowania warstwy dostępu do danych [DAO]. W tym celu duplikujemy projekt konsoli VS 2012 [RdvMedecins-SqlServer-02] w [RdvMedecins-PostgreSQL-02] [1]:
![]() |
- do [2], usuwamy projekt [RdvMedecins-SqlServer-02];
![]() |
- w [3] dodajemy istniejący projekt do rozwiązania. Pobieramy go z właśnie utworzonego folderu [RdvMedecins-PostgreSQL-02];
- w [4] nowy projekt nosi nazwę tego, który został usunięty. Zmienimy jego nazwę;
![]() |
- w pliku [5] zmieniono nazwę projektu;
- w [6] zmieniono niektóre właściwości, np. nazwę zestawu;
- na [7], folder [Models] został usunięty i zastąpiony folderem [Models] z projektu [RdvMedecins-PostgreSQL-01]. Oba projekty korzystają bowiem z tych samych szablonów.
![]() |
- w [8] – aktualne odniesienia projektu;
- w pliku [9] dodano łącznik ADO.NET z projektu PostgreSQL za pomocą narzędzia NuGet.
W pliku [App.config] zastępuje się informacje z bazy SQL Server informacjami z bazy PostgreSQL. Znajdują się one w pliku [App.config] projektu [RdvMedecins-PostgreSQL-01]:
<!-- łańcuch połączenia z bazą danych -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- dostawca fabryczny -->
<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>
Zmieniają się również obiekty zarządzane przez Spring. Obecnie mamy:
<!-- konfiguracja Spring -->
<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>
Wiersz 7 odwołuje się do zestawu projektu [RdvMedecins-SqlServer-02]. Zestaw ten nosi teraz nazwę [RdvMedecins-PostgreSQL-02].
Po wykonaniu tej czynności jesteśmy gotowi do przeprowadzenia testu warstwy [DAO]. Wcześniej należy jednak zadbać o wypełnienie bazy danych (program [Fill] z projektu [RdvMedecins-PostgreSQL-01]). Program testowy ulega awarii, zgłaszając następujący wyjątek:
Wiersz 13 – komunikat wskazuje, że błąd wystąpił w metodzie [GetCreneauxMedecin] warstwy [DAO]. Metoda ta ma następującą postać:
// lista terminów wizyt danego lekarza
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista terminów
try
{
// otwarcie kontekstu trwałości
using (var context = new RdvMedecinsContext())
{
// pobieranie lekarza wraz z jego terminami
Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
// zwracanie listy terminów lekarza
return medecin.Creneaux.ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
W wierszu 11 rozpoznajemy słowo kluczowe Include, które już wcześniej spowodowało awarię innego programu. Poprzedni kod można zastąpić następującym:
// lista terminów danego lekarza
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// lista terminów
try
{
// otwarcie kontekstu trwałości
using (var context = new RdvMedecinsContext())
{
// zwraca listę terminów lekarza
return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
Nowy kod wydaje się nawet bardziej spójny niż poprzedni. W każdym razie tym razem program testowy działa poprawnie.
Tworzymy plik DLL projektu tak samo, jak zrobiono to dla projektu [RdvMedecins-SqlServer-02], a następnie przenosimywszystkie pliki DLL z projektu w folderze [lib] utworzonym w [RdvMedecins-PostgreSQL-02]. Będą to odniesienia do projektu internetowego [RdvMedecins-PostgreSQL-03], który pojawi się w dalszej części.
![]() |
Jesteśmy teraz gotowi do zbudowania warstwy [ASP.NET] naszej aplikacji:
![]() |
Wychodzimy od projektu [RdvMedecins-SqlServer-03]. Duplikujemy folder tego projektu do [RdvMedecins-PostgreSQL-03] i [1]:
![]() |
- do [2], przy użyciu programu VS 2012 Express for the Web otwieramy rozwiązanie z folderu [RdvMedecins-PostgreSQL-03];
- w pliku [3] zmieniamy zarówno nazwę rozwiązania, jak i nazwę projektu;
![]() |
- w pliku [4] aktualne odniesienia projektu;
- w pliku [5] usuwamy je;
- na [6], aby zastąpić je odwołaniami do pliku DLL, który właśnie zapisaliśmy w folderze [lib] projektu [RdvMedecins-PostgreSQL-02].
Pozostaje nam tylko zmodyfikować plik [Web.config]. Zastępujemy jego aktualną zawartość zawartością pliku [App.config] z projektu [RdvMedecins-PostgreSQL-02]. Po wykonaniu tej czynności uruchamiamy projekt internetowy. Działa. Nie zapomnijmy o zapełnieniu bazy danych przed uruchomieniem aplikacji internetowej.


























