Skip to content

7. Studium przypadku z wykorzystaniem Firebird 2.1

7.1. Instalacja narzędzi

Należy zainstalować następujące narzędzia:

  • SGBD: [http://www.firebirdsql.org/en/firebird-2-1-5/];
  • narzędzie administracyjne: EMS, SQL Manager dla InterBase/Firebird Freeware, [http://www.sqlmanager.net/fr/products/ibfb/manager/download].

W poniższych przykładach użytkownikiem jest sysdba z hasłem masterkey.

Uruchommy Firebird, a następnie narzędzie [SQL Manager Lite for Firebird], za pomocą którego będziemy zarządzać SGBD.

  • w [1] uruchamiamy Firebird SGBD z menu Start. W tym przypadku SGBD nie został zainstalowany jako usługa systemu Windows;
  • w [2] usługa została uruchomiona. W prawym dolnym rogu ekranu pojawiła się ikona. Klikając na nią prawym przyciskiem myszy, można zatrzymać SGBD.

Teraz uruchamiamy narzędzie [SQL Manager Lite for Firebird], za pomocą którego będziemy zarządzać SGBD i [3].

  • w [4] tworzymy nową bazę;
  • w [5] zatwierdzamy;
  • w [5] logujemy się jako SYSDBA / masterkey;
  • w [6] wskazujemy lokalizację pliku, który zostanie utworzony. Baza danych zostanie bowiem utworzona w jednym pliku;
  • w [7] zatwierdza się polecenie SQL, które zostanie wykonane;
  • w [8] baza została utworzona. Teraz należy ją zapisać 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.

Teraz podłączymy do tej bazy projekt VS 2012.

7.2. Tworzenie bazy danych na podstawie obiektów

Zaczynamy od skopiowania folderu projektu [RdvMedecins-SqlServer-01] do [RdvMedecins-Firebird-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-Firebird-01], który utworzyliśmy wcześniej;
  • w pliku [5] załadowany projekt nosi nazwę [RdvMedecins-SqlServer-01];
  • w [6] zmieniamy jego nazwę na [RdvMedecins-Firebird-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 pliku [8] projekt [RdvMedecins-SqlServer-01] został ponownie dodany do rozwiązania.

Projekt [RdvMedecins-Firebird-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="User=SYSDBA;Password=masterkey;Database=D:\data\istia-1213\c#\dvp\Entity Framework\databases\firebird\RDVMEDECINS-EF.GDB;DataSource=localhost;
Port=3050;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0;" providerName="FirebirdSql.Data.FirebirdClient" />
  </connectionStrings>
  <!-- dostawca fabryczny -->
  <system.data>
    <DbProviderFactories>
      <add name="Firebird Client Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description=".Net Framework Data Provider for Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient, Version=2.7.7.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
    </DbProviderFactories>
  </system.data>
  • wiersz 3: nazwa użytkownika i hasło, a także pełna ścieżka do bazy danych Firebird;
  • wiersze 8–10: plik DbProviderFactory. Wiersz 9 odwołuje się do pliku DLL [FirebirdSql.Data.FirebirdClient], którego nie posiadamy. Można go uzyskać za pomocą plików NuGet i [1]:
  • w [2], w polu wyszukiwania wpisujemy słowo kluczowe firebird;
  • w przypadku [3] należy wybrać pakiet [Firebird ADO.NET Data Provider]. Jest to łącznik ADO.NET dla Firebirda;
  • w [4], nowy numer referencyjny;
  • w [5], w [App.config] należy wpisać prawidłową wersję DLL. Znajduje się ona w jego właściwościach.

W pliku [Entites.cs] należy dostosować schemat tabel, które zostaną wygenerowane:


  [Table("MEDECINS")]
  public class Medecin : Personne
  {...}

  [Table("CLIENTS")]
  public class Client : Personne
  {...}

  [Table("CRENEAUX")]
  public class Creneau
  {...}

  [Table("RVS")]
  public class Rv
  {...}

W tym przypadku tabele nie mają schematu.

Konfigurujemy wykonanie projektu:

  • w [1] nadajemy inną nazwę generowanemu zestawowi;
  • w [2] określamy również 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:

Exception non gérée : System.Data.MetadataException: Le schéma spécifié n'est pas valide. Erreurs :
(11,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(23,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(33,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(43,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
   à System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors
()
   ...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-Oracle-01\CreateDB_01.cs:ligne 15

Pamiętamy, że ten sam błąd wystąpił w przypadku MySQL, Oracle oraz PostgreSQL. Jest to związane z typem pola Timestamp w encjach. Wprowadzamy tę samą zmianę, co w przypadku dwóch poprzednich pól SGBD. 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 więc 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:

1
2
3
4
5
Exception non gérée : FirebirdSql.Data.FirebirdClient.FbException: lock time-out on wait transaction object D:\DATA\ISTIA-1213\C#\DVP\ENTITY FRAMEWORK\DATABASES\FIREBIRD\RDVMEDECINS-EF.GDB jest w użyciu ---> FirebirdSql.Data.Common.IscException: przekroczono limit czasu blokady podczas oczekiwania na transakcję
object D:\DATA\ISTIA-1213\C#\DVP\ENTITY FRAMEWORK\DATABASES\FIREBIRD\RDVMEDECINS
-EF.GDB is in use
...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\dvp\Entity Framework\RdvMedecins\RdvMedecins-Firebird-01\CreateDB_01.cs: wiersz 15

Wiersz 1 wskazuje, że baza jest w użyciu. Wydaje mi się, że tak nie było i nie udało mi się rozwiązać tego problemu.

Nieważne. Stworzymy bazę danych [RDVMEDECINS-EF] ręcznie za pomocą narzędzia [EMS Manager for Firebird]. Nie opisujemy wszystkich kroków, a jedynie te najważniejsze.

Baza danych Firebird będzie miała następujący wygląd:

Tabele

  • w [1], ID jest kluczem głównym z atrybutem Autoincrement. Zostanie ona wygenerowana 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.

Generatory

Podobnie jak w przypadku Oracle i PostgreSQL, utworzyliśmy generatory liczb kolejnych. Jest ich 5: [1].

  • [CLIENTS_ID_GEN] będzie używany do generowania klucza głównego tabeli [CLIENTS];
  • [MEDECINS_ID_GEN] zostanie wykorzystany do wygenerowania klucza głównego tabeli [MEDECINS];
  • [CRENEAUX_ID_GEN] zostanie wykorzystany do wygenerowania klucza głównego tabeli [CRENEAUX];
  • [RVS_ID_GEN] zostanie użyty do wygenerowania klucza głównego tabeli [RVS];
  • [VERSIONS_GEN] zostanie użyty 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 ich 8 [1]:

Przyjrzyjmy się kodowi DDL wyzwalacza [BI_CLIENTS_ID], który zasilają kolumnę [ID] w tabeli [CLIENTS]:

1
2
3
4
5
6
7
8
CREATE TRIGGER BI_CLIENTS_ID FOR CLIENTS
ACTIVE BEFORE INSERT
POSITION 0
AS
BEGIN
  IF (NEW.ID IS NULL) THEN
      NEW.ID = GEN_ID(CLIENTS_ID_GEN, 1);
END^
  • wiersz 2: przed każdym wstawieniem do tabeli [CLIENTS];
  • wiersze 6–7: jeśli kolumna ID ma wartość NULL, wówczas przypisuje się jej następną wartość z generatora liczb [CLIENTS_ID_GEN].

Wszystkie wyzwalacze [ BI_CLIENTS_ID, BI_MEDECINS_ID, BI_CRENEAUX_ID, BI_RVS_ID] są zbudowane w ten sam sposób.

Przyjrzyjmy się teraz kodowi DDL wyzwalacza [CLIENTS_VERSION_TRIGGER], który zasila kolumnę [VERSIONING] w tabeli [CLIENTS]:

1
2
3
4
5
6
7
CREATE TRIGGER CLIENTS_VERSION_TRIGGER FOR CLIENTS
ACTIVE BEFORE INSERT OR UPDATE
POSITION 1
AS
BEGIN
  NEW."VERSIONING" = GEN_ID(VERSIONS_GEN,1);
END^
  • wiersze 1–3: przed każdą operacją INSERT lub UPDATE na tabeli [CLIENTS];
  • wiersz 6: kolumna ["VERSIONING"] otrzymuje następującą wartość z generatora liczb [VERSIONS_GEN]. Generator ten zasila kolumny ["VERSIONING"] we wszystkich czterech tabelach.

Wyzwalacze [MEDECINS_VERSION_TRIGGER, CRENEAUX_VERSION_TRIGGER, RVS_VERSION_TRIGGER] działają na podobnej zasadzie.

Skrypt generujący tabele bazy danych Firebird o nazwie [RDVMEDECINS-EF] został umieszczony w folderze [RdvMedecins / databases / Firebird]. Czytelnik może go załadować i uruchomić w celu utworzenia swoich tabel.

Po wykonaniu tej czynności można uruchomić różne 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, z jakiego ulegał awarii w przypadku Oracle i MySQL. Problem rozwiązuje się w ten sam sposób. Wystarczy skopiować program [ModifyDetachedEntities] z projektu [RdvMedecins-Oracle-01] do projektu [RdvMedecins-Firebird-01].

7.3. Architektura wielowarstwowa oparta na EF 5

Wracamy do naszego studium przypadku opisanego w akapicie 2.

Zaczniemy od utworzenia warstwy dostępu do danych o nazwie [DAO]. W tym celu duplikujemy projekt konsoli VS 2012 [RdvMedecins-SqlServer-02] w [RdvMedecins-Firebird-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-Firebird-02];
  • w [4] nowy projekt nosi nazwę tego, który został usunięty. Zmienimy jego nazwę;
  • w [5] zmieniono nazwę projektu;
  • w [6] modyfikujemy niektóre jego właściwości, jak w tym przypadku nazwę zestawu;
  • w [7] folder [Models] został usunięty i zastąpiony folderem [Models] z projektu [RdvMedecins-Firebird-01]. Oba projekty korzystają bowiem z tych samych szablonów.
  • w [8] – aktualne odniesienia projektu;
  • w [9] dodano łącznik ADO.NET dla Firebirda za pomocą narzędzia NuGet.

W pliku [App.config] należy zastąpić informacje dotyczące serwera bazy danych SQL danymi z bazy Firebird. Znajdują się one w pliku [App.config] projektu [RdvMedecins-Firebird-01]:


<!-- ciąg połączenia z bazą danych -->
  <connectionStrings>
    <add name="monContexte" connectionString="User=SYSDBA;Password=masterkey;Database=D:\data\istia-1213\c#\dvp\Entity Framework\databases\firebird\RDVMEDECINS-EF.GDB;DataSource=localhost;
Port=3050;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0;" providerName="FirebirdSql.Data.FirebirdClient" />
  </connectionStrings>
  <!-- dostawca fabryki -->
  <system.data>
    <DbProviderFactories>
      <add name="Firebird Client Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description=".Net Framework Data Provider for Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient, Version=2.7.7.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
    </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-Firebird-02].

Po wykonaniu tej czynności jesteśmy gotowi do przeprowadzenia testu warstwy [DAO]. Najpierw należy zadbać o wypełnienie bazy danych (program [Fill] z projektu [RdvMedecins-Firebird-01]). Test przebiega pomyślnie.

Tworzymy plik DLL projektu w taki sam sposób, jak w przypadku projektu [RdvMedecins-SqlServer-02], a następnie przenosimywszystkie pliki DLL z projektu w folderze [lib] utworzonym w [RdvMedecins-Firebird-02]. Będą to pliki referencyjne dla projektu internetowego [RdvMedecins-Firebird-03], który zostanie zrealizowany w następnej kolejności.

  

Jesteśmy teraz gotowi do zbudowania warstwy [ASP.NET] naszej aplikacji:

Wychodzimy od projektu [RdvMedecins-SqlServer-03]. Duplikujemy folder tego projektu do [RdvMedecins-Firebird-03] i [1]:

  • do [2], przy użyciu programu VS 2012 Express for the Web otwieramy rozwiązanie z folderu [RdvMedecins-Firebird-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-Firebird-02].

Pozostaje nam tylko zmodyfikować plik [Web.config]. Zastępujemy jego aktualną zawartość zawartością pliku [App.config] z projektu [RdvMedecins-Firebird-02]. Po wykonaniu tej czynności uruchamiamy projekt internetowy. Działa. Nie zapomnijmy o zapełnieniu bazy danych przed uruchomieniem aplikacji internetowej.