5. Studium przypadku z wykorzystaniem Oracle Database Express Edition 11g Release 2
5.1. Instalacja narzędzi
Należy zainstalować następujące narzędzia:
- SGBD: [http://www.oracle.com/technetwork/products/express-edition/downloads/index.html];
- narzędzie administracyjne: EMS SQL Manager for Oracle Freeware [http://www.sqlmanager.net/fr/products/oracle/manager/download];
- klient Oracle dla .NET: ODAC 11.2 Release 5 (11.2.0.3.20) z narzędziami Oracle Developer Tools dla Visual Studio: [http://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.html].
W poniższych przykładach użytkownik „system” ma hasło „system”.
Uruchommy Oracle [1], a następnie narzędzie [SQL Manager Lite for Oracle], za pomocą którego będziemy zarządzać SGBD i [2].
![]() |
- w [3] łączymy się z istniejącą bazą danych;
![]() |
- w [4] korzystamy z usługi Oracle XE w celu nawiązania połączenia;
- w [5] podajemy nazwę bazy danych XE;
- w [6] logujemy się jako system / system;
- w [7] zamykamy kreatora;
![]() |
- w [8] logujemy się do bazy danych;
- w [9] jesteśmy zalogowani;
- ponieważ zalogowaliśmy się jako użytkownik system / system, który ma rozszerzone uprawnienia, będziemy mogli na przykład zarządzać użytkownikami [10];
![]() |
- w [11] tworzymy nowego użytkownika;
- o nazwie [12], będzie on nosił nazwę [RDVMEDECINS-EF];
- w [13], a jego hasło to rdvmedecins;
- w [14] zatwierdzamy utworzenie użytkownika;
- w [15] użytkownik został utworzony;
![]() |
- w [16] użytkownik [RDVMEDECINS-EF] jest również schematem bazy danych;
- w [17] utworzony użytkownik nie posiada wystarczających uprawnień. Przyznajemy mu je za pomocą skryptu SQL;
![]() |
- w [18] skrypt został uruchomiony;
- w [19] spróbujemy zalogować się pod tożsamością [RDVMEDECINS-EF], aby sprawdzić, jakie ma uprawnienia. W tym celu najpierw rejestrujemy nową bazę w [EMS Manager];
![]() |
- w [19] logujemy się za pośrednictwem usługi XE;
- w [20] logujemy się pod identyfikatorem RDVMEDECINS-EF / rdvmedecins;
- w [21] podaje się alias odzwierciedlający nazwę zalogowanego użytkownika;
- w [22] logujemy się do Oracle przy użyciu podanych danych;
![]() |
![]() |
- w schemacie [22] udało się nawiązać połączenie;
- w [23] próbujemy utworzyć tabelę w schemacie [RDVMEDECINS-EF];
- w [24] definiujemy dowolną tabelę;
- w [25] zatwierdzamy jej definicję;
![]() |
- w [26] tabela została utworzona. Usuwamy ją;
- w [27] została usunięta.
Teraz, gdy mamy użytkownika z odpowiednimi uprawnieniami, utworzymy projekt VS 2012, który utworzy tabele schematu [RDVMEDECINS-EF] na podstawie definicji encji.
5.2. Tworzenie bazy danych na podstawie encji
Zaczynamy od skopiowania folderu projektu [RdvMedecins-SqlServer-01] do [RdvMedecins-Oracle-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-Oracle-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-Oracle-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-Oracle-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="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
<!-- dostawca fabryczny -->
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.DataAccess.Client" />
<add name="Oracle Data Provider for .NET" invariant="Oracle.DataAccess.Client" description="Oracle Data Provider for .NET" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
</DbProviderFactories>
</system.data>
- wiersz 3: nazwa użytkownika i hasło;
- wiersze 6–11: plik DbProviderFactory. Wiersz 9 odwołuje się do pliku DLL i [Oracle.DataAccess], których nie posiadamy. Można go uzyskać za pomocą plików NuGet i [1]:
![]() |
- w [2], w polu wyszukiwania wpisujemy słowo kluczowe oracle;
- w przypadku [3] należy wybrać odpowiedni pakiet [Oracle Data Provider]. Jest to łącznik ADO.NET firmy Oracle;
![]() |
- w [4] dodano numer referencyjny;
- w [5], w [App.config] należy wpisać prawidłową wersję z DLL. Można ją znaleźć w jego właściwościach.
W pliku [Entites.cs] należy dostosować schemat tabel, które zostaną wygenerowane. Używany schemat to nazwa użytkownika będącego właścicielem tabel.
[Table("MEDECINS", Schema = "RDVMEDECINS-EF")]
public class Medecin : Personne
{...}
[Table("CLIENTS", Schema = "RDVMEDECINS-EF")]
public class Client : Personne
{...}
[Table("RVS", Schema = "RDVMEDECINS-EF")]
public class Rv
{...}
[Table("CRENEAUX", Schema = "RDVMEDECINS-EF")]
public class Creneau
{...}
Konfigurujemy realizację 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:
Pamiętamy, że ten sam błąd wystąpił w przypadku programu MySQL. Jest to związane z typem pola Timestamp w encjach. Wprowadzamy tę samą zmianę. 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 zatem typ kolumny z byte[] na int?. Przypomnijmy, że zarówno w przypadku SQL Server, jak i MySQL, kolumna w tabelach służąca do zarządzania współbieżnością dostępu otrzymywała wartość z SGBD za każdym razem, gdy wstawiano lub modyfikowano wiersz. Od tej pory będziemy używać pola encji, które będzie liczbą całkowitą. W SGBD będziemy korzystać z procedur przechowywanych, 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 Oracle o nazwie ADO.NET nie jest w stanie usunąć istniejącej bazy danych. Przypomnijmy, co się dzieje. Kod [CreateDB_01.cs] wygląda następująco:
using System;
using System.Data.Entity;
using RdvMedecins.Models;
namespace RdvMedecins_01
{
class CreateDB_01
{
static void Main(string[] args)
{
// tworzymy bazę danych
Database.SetInitializer(new RdvMedecinsInitializer());
using (var context = new RdvMedecinsContext())
{
context.Database.Initialize(false);
}
}
}
}
Wiersz 15 uruchamia wykonanie klasy [RdvMedecinsInitializer] (wiersz 12). Wygląda ona następująco:
public class RdvMedecinsInitializer : DropCreateDatabaseAlways<RdvMedecinsContext>
Wywodzi się ona z klasy [DropCreateDatabaseAlways], która próbuje usunąć, a następnie odtworzyć bazę danych. Zmieniamy definicję klasy na:
public class RdvMedecinsInitializer : CreateDatabaseIfNotExists<RdvMedecinsContext>
Baza danych jest tworzona tylko wtedy, gdy nie istnieje. Ponownie uruchamiamy [CreateDB_01.cs] i tym razem nie ma już błędów. Jednak w [EMS Manager] zauważamy, że baza danych [RDVMEDECINS-EF] pozostała pusta. Ponieważ program EF 5 znalazł istniejącą bazę, nie wykonał żadnych czynności. Działa on tylko wtedy, gdy baza nie istnieje. Od tego momentu sytuacja staje się błędnym kołem. W rzeczywistości ciąg połączenia dla programu SGBD wygląda następująco:
<connectionStrings>
<add name="monContexte" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
W wierszu 2 ciąg połączenia nie zawiera nazwy bazy danych, lecz nazwę użytkownika. Użytkownik ten musi istnieć.
Jesteśmy zatem zmuszeni do ręcznego utworzenia bazy danych [RDVMEDECINS-EF] za pomocą narzędzia [EMS Manager for Oracle]. Nie opisujemy wszystkich etapów, a jedynie te najważniejsze.
Baza danych Oracle będzie miała następujący wygląd:
Tabele
![]() |
Poszczególne tabele posiadają klucze główne i obce, które te same tabele miały w dwóch poprzednich przykładach. Klucze obce mają w szczególności atrybuty ON, DELETE oraz CASCADE.
Sekwencje
Utworzono tutaj sekwencje Oracle. Są to generatory kolejnych liczb. Jest ich 5: [1].
![]() |
- W sekwencji [2] widzimy właściwości sekwencji [SEQUENCE_CLIENTS]. 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 [SEQUENCE_CLIENTS] zostanie wykorzystana do wygenerowania klucza głównego tabeli [CLIENTS];
- [SEQUENCE_MEDECINS] zostanie wykorzystana do wygenerowania klucza głównego tabeli [MEDECINS];
- [SEQUENCE_CRENEAUX] zostanie wykorzystany do wygenerowania klucza głównego tabeli [CRENEAUX];
- [SEQUENCE_RVS] 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 ich 8 [1]:
![]() |
Przyjrzyjmy się kodowi DDL wyzwalacza [TRIGGER_PK_CLIENTS], który zasila klucz główny tabeli [CLIENTS]:
- wiersze 1–5: przed każdą operacją INSERT na tabeli [CLIENTS];
- wiersz 6: kolumna [ID] przyjmie następną wartość z sekwencji [SEQUENCE_CLIENTS]. Klucz główny będzie w ten sposób posiadał kolejne wartości dostarczane przez sekwencję.
Wyzwalacze [TRIGGER_PK_MEDECINS, TRIGGER_PK_CRENEAUX, TRIGGER_PK_RVS] działają analogicznie.
Przyjrzyjmy się kodowi DDL wyzwalacza [TRIGGER_VERSIONS_CLIENTS], który zasila kolumnę [VERSIONING] w tabeli [CLIENTS]:
- wiersze 1–2: przed każdą operacją INSERT lub UPDATE na tabeli [CLIENTS];
- wiersz 8: kolumna [VERSIONING] przyjmie następną wartość z sekwencji [SEQUENCE_VERSIONS]. Kolumna [VERSIONING] będzie w ten sposób zawierała kolejne wartości dostarczane przez sekwencję.
Wyzwalacze [TRIGGER_VERSION_MEDECINS, TRIGGER_VERSION_CRENEAUX, TRIGGER_VERSION_RVS] działają analogicznie. Cztery kolumny [VERSIONING] pobierają swoje wartości z tej samej sekwencji.
Skrypt generujący tabele bazy danych Oracle o nazwie [RDVMEDECINS-EF] został umieszczony w folderze [RdvMedecins / databases / oracle]. Czytelnik 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 MySQL. Problem rozwiązuje się w ten sam sposób. Wystarczy skopiować program [ModifyDetachedEntities] z projektu [RdvMedecins-MySQL-01] do projektu [RdvMedecins-Oracle-01]. Pojawia się wówczas nowy problem:
- wiersze 1–4: odłączony klient został poprawnie zaktualizowany;
- wiersz 6: znany wyjątek. Występuje on, gdy próbuje się zmodyfikować encję, nie mając odpowiedniej wersji. Jednak w tym przypadku nie chcieliśmy modyfikować, lecz usunąć encję:
// usunięcie encji poza kontekstem
using (var context = new RdvMedecinsContext())
{
// tutaj mamy nowy pusty kontekst
// umieszczamy klienta1 w kontekście w stanie usuniętym
context.Entry(client1).State = EntityState.Deleted;
// zapisujemy kontekst
context.SaveChanges();
}
EF 5 odmówił usunięcia client1 z bazy danych, ponieważ client1 (wiersz 6) nie miał tej samej wersji. Nie napotkaliśmy tego problemu w przypadku MySQL. Stopniowo zauważamy, że łączniki ADO.NET z różnych SGBD wykazują niewielkie różnice. Poprawiamy to w następujący sposób:
using (var context = new RdvMedecinsContext())
{
// tutaj mamy nowy pusty kontekst
// umieszczamy „client1” w kontekście w celu jego usunięcia
context.Clients.Remove(context.Clients.Find(client1.Id));
// zapisujemy kontekst
context.SaveChanges();
}
i to działa.
5.3. Architektura wielowarstwowa oparta na EF 5
Wracamy do naszego studium przypadku opisanego w akapicie 2 na stronie 7.
![]() |
Zaczniemy od zbudowania warstwy dostępu do danych [DAO]. W tym celu powielamy projekt konsoli VS 2012 [RdvMedecins-SqlServer-02] w [RdvMedecins-Oracle-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-Oracle-02];
- w [4] nowy projekt nosi nazwę tego, który został usunięty. Zmienimy jego nazwę;
![]() |
- w [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-Oracle-01]. Oba projekty korzystają bowiem z tych samych szablonów.
![]() |
- w [8] – aktualne odniesienia projektu;
- w [9] dodano łącznik Oracle ADO.NET za pomocą narzędzia NuGet.
W pliku [App.config] zastępuje się dane z bazy SQL Server danymi z bazy Oracle. Znajdują się one w pliku [App.config] projektu [RdvMedecins-Oracle-01]:
<!-- łańcuch połączenia z bazą danych -->
<connectionStrings>
<add name="monContexte" connectionString="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=RDVMEDECINS-EF;Password=rdvmedecins;" providerName="Oracle.DataAccess.Client" />
</connectionStrings>
<!-- dostawca fabryki -->
<system.data>
<DbProviderFactories>
<remove invariant="Oracle.DataAccess.Client" />
<add name="Oracle Data Provider for .NET" invariant="Oracle.DataAccess.Client" description="Oracle Data Provider for .NET" type="Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" />
</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-Oracle-02].
Po wykonaniu tej czynności jesteśmy gotowi do przeprowadzenia testu warstwy [DAO]. Wcześniej należy zadbać o wypełnienie bazy danych (program [Fill] z projektu [RdvMedecins-Oracle-01]). Test programu zakończył się powodzeniem.
Tworzymy plik DLL projektu w taki sam sposób, jak w przypadku projektu [RdvMedecins-SqlServer-02], a następnie gromadzimywszystkie pliki DLL z projektu w folderze [lib] utworzonym w [RdvMedecins-Oracle-02]. Będą to pliki referencyjne dla projektu internetowego [RdvMedecins-Oracle-03], który zostanie zrealizowany w następnej kolejności.
![]() |
Jesteśmy teraz gotowi do stworzenia warstwy [ASP.NET] naszej aplikacji:
![]() |
Wychodzimy od projektu [RdvMedecins-SqlServer-03]. Duplikujemy folder tego projektu w [RdvMedecins-Oracle-03] i [1]:
![]() |
- do [2], przy użyciu programu VS 2012 Express for the Web otwieramy rozwiązanie z folderu [RdvMedecins-Oracle-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-Oracle-02].
Pozostaje nam tylko zmodyfikować plik [Web.config]. Zastępujemy jego aktualną zawartość zawartością pliku [App.config] z projektu [RdvMedecins-Oracle-02]. Po wykonaniu tej czynności uruchamiamy projekt internetowy. Działa. Nie zapomnijmy o zapełnieniu bazy danych przed uruchomieniem aplikacji internetowej.




























