5. Caso di studio con Oracle Database Express Edition 11g Release 2
5.1. Installazione degli strumenti
Gli strumenti da installare sono i seguenti:
- il DBMS: [http://www.oracle.com/technetwork/products/express-edition/downloads/index.html];
- uno strumento di amministrazione: EMS SQL Manager for Oracle Freeware [http://www.sqlmanager.net/fr/products/oracle/manager/download];
- un client Oracle per .NET: ODAC 11.2 Release 5 (11.2.0.3.20) con Oracle Developer Tools per Visual Studio: [http://www.oracle.com/technetwork/developer-tools/visual-studio/downloads/index.html].
Negli esempi seguenti, l'utente "system" ha la password "system".
Avviare Oracle [1] e quindi lo strumento [SQL Manager Lite for Oracle], che useremo per amministrare il DBMS [2].
![]() |
- In [3], ci colleghiamo a un database esistente;
![]() |
- In [4], utilizziamo il servizio Oracle XE per la connessione;
- in [5], specifichiamo il nome del database XE;
- in [6], effettuiamo l'accesso come system/system;
- In [7], completiamo la procedura guidata;
![]() |
- in [8], ci colleghiamo al database;
- In [9], siamo connessi;
- Poiché abbiamo effettuato l'accesso come utente system/system, che dispone di privilegi estesi, possiamo, ad esempio, gestire gli utenti [10];
![]() |
- In [11], creare un nuovo utente;
- in [12], l'utente si chiamerà [RDVMEDECINS-EF];
- in [13], e avrà la password rdvmedecins;
- In [14], la creazione dell'utente è confermata;
- in [15], l'utente è stato creato;
![]() |
- in [16], l'utente [RDVMEDECINS-EF] è anche uno schema del database;
- in [17], l'utente così creato non dispone di autorizzazioni sufficienti. Le concediamo tramite uno script SQL;
![]() |
- in [18], lo script viene eseguito;
- in [19], proveremo ad accedere come [RDVMEDECINS-EF] per vedere cosa può fare. Per farlo, iniziamo registrando un nuovo database in [EMS Manager];
![]() |
- in [19], effettuiamo l'accesso tramite il servizio XE;
- in [20], effettuiamo l'accesso come RDVMEDECINS-EF / rdvmedecins;
- In [21], assegniamo un alias che riflette il nome dell'utente che ha effettuato l'accesso;
- in [22], ci connettiamo a Oracle utilizzando le credenziali fornite;
![]() |
![]() |
- In [22], abbiamo effettuato l'accesso con successo;
- In [23], tentiamo di creare una tabella nello schema [RDVMEDECINS-EF];
- in [24], definiamo una tabella;
- In [25], ne convalidiamo la definizione;
![]() |
- In [26], la tabella è stata creata. La eliminiamo;
- In [27], è stata eliminata.
Ora che disponiamo di un utente con autorizzazioni sufficienti, creeremo il progetto VS 2012 che genererà le tabelle nello schema [RDVMEDECINS-EF] in base alle definizioni delle entità.
5.2. Creazione del database dalle entità
Iniziamo duplicando la cartella del progetto [RdvMedecins-SqlServer-01] in [RdvMedecins-Oracle-01] [1]:
![]() |
- in [2], in VS 2012, rimuoviamo il progetto [RdvMedecins-SqlServer-01] dalla soluzione;
![]() |
- in [3], il progetto è stato rimosso;
- in [4], ne aggiungiamo un altro. Questo proviene dalla cartella [RdvMedecins-Oracle-01] che abbiamo creato in precedenza;
![]() |
- in [5], il progetto caricato si chiama [RdvMedecins-SqlServer-01];
- in [6], ne modifichiamo il nome in [RdvMedecins-Oracle-01]
![]() |
- in [7], aggiungiamo un altro progetto alla soluzione. Questo proviene dalla cartella [RdvMedecins-SqlServer-01] del progetto che abbiamo precedentemente rimosso dalla soluzione;
- in [8], il progetto [RdvMedecins-SqlServer-01] è stato aggiunto nuovamente alla soluzione.
Il progetto [RdvMedecins-Oracle-01] è identico al progetto [RdvMedecins-SqlServer-01]. Dobbiamo apportare alcune modifiche. In [App.config], modificheremo la stringa di connessione e il [DbProviderFactory], che devono essere adattati a ciascun DBMS.
<!-- connection chain on base -->
<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>
<!-- the factory provider -->
<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>
- riga 3: nome utente e password;
- righe 6–11: DbProviderFactory. La riga 9 fa riferimento a una DLL [Oracle.DataAccess] che non abbiamo. Possiamo ottenerla utilizzando NuGet [1]:
![]() |
- in [2], digitare la parola chiave "oracle" nella casella di ricerca;
- in [3], selezionare il pacchetto appropriato [Oracle Data Provider]. Si tratta del connettore ADO.NET di Oracle;
![]() |
- in [4], il riferimento viene aggiunto;
- in [5], in [App.config], è necessario specificare la versione corretta della DLL. È possibile trovarla nelle sue proprietà.
Nel file [Entities.cs], è necessario adattare lo schema delle tabelle che verranno generate. Lo schema utilizzato è il nome dell'utente proprietario delle tabelle.
[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
{...}
Configuriamo la compilazione del progetto:
![]() |
- in [1], assegniamo un nome diverso all'assembly che verrà generato;
- in [2], specifichiamo anche uno spazio dei nomi predefinito diverso;
- in [3], specifichiamo il programma da eseguire.
A questo punto non ci sono errori di compilazione. Eseguiamo il programma [CreateDB_01]. Otteniamo la seguente eccezione:
Ricordiamo di aver riscontrato lo stesso errore con MySQL. Ciò è legato al tipo del campo Timestamp nelle entità. Apportiamo la stessa modifica. Nelle entità, sostituiamo le tre righe
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
con quanto segue:
[ConcurrencyCheck]
[Column("VERSIONING")]
public int? Versioning { get; set; }
Modificheremo quindi il tipo della colonna da byte[] a int?. Ricordiamo che, sia in SQL Server che in MySQL, al campo della tabella utilizzato per gestire la concorrenza degli accessi veniva assegnato un valore dal DBMS ogni volta che veniva inserita o modificata una riga. D'ora in poi, utilizzeremo un campo dell'entità di tipo intero. Nel DBMS, ricorreremo a procedure memorizzate per incrementare di uno questo numero intero ogni volta che viene inserita o modificata una riga.
Applichiamo la modifica di cui sopra a tutte e quattro le entità e quindi rieseguiamo l'applicazione. A questo punto riceviamo il seguente errore:
La riga 1 indica che il connettore Oracle ADO.NET non è in grado di eliminare il database esistente. Esaminiamo cosa sta succedendo. Il codice in [CreateDB_01.cs] è il seguente:
using System;
using System.Data.Entity;
using RdvMedecins.Models;
namespace RdvMedecins_01
{
class CreateDB_01
{
static void Main(string[] args)
{
// create the database
Database.SetInitializer(new RdvMedecinsInitializer());
using (var context = new RdvMedecinsContext())
{
context.Database.Initialize(false);
}
}
}
}
La riga 15 avvia l'esecuzione della classe [RdvMedecinsInitializer] (riga 12). Questa classe è la seguente:
public class RdvMedecinsInitializer : DropCreateDatabaseAlways<RdvMedecinsContext>
Deriva dalla classe [DropCreateDatabaseAlways], che tenta di eliminare e quindi ricreare il database. Modifichiamo la definizione della classe come segue:
public class RdvMedecinsInitializer : CreateDatabaseIfNotExists<RdvMedecinsContext>
Il database viene creato solo se non esiste. Eseguiamo nuovamente [CreateDB_01.cs] e non ci sono più errori. Tuttavia, in [EMS Manager], vediamo che il database [RDVMEDECINS-EF] rimane vuoto. Poiché EF 5 ha trovato un database esistente, non ha fatto nulla. Interviene solo se il database non esiste. A questo punto, siamo bloccati in un loop. Infatti, la stringa di connessione al DBMS è la seguente:
<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>
Riga 2: La stringa di connessione utilizza un nome utente anziché un nome di database. Questo utente deve esistere.
Dobbiamo quindi creare manualmente il database [RDVMEDECINS-EF] utilizzando lo strumento [EMS Manager for Oracle]. Non descriveremo ogni passaggio, ma solo quelli più importanti.
Il database Oracle sarà il seguente:
Le tabelle
![]() |
Le varie tabelle presentano le chiavi primarie e le chiavi esterne presenti nelle stesse tabelle dei due esempi precedenti. Le chiavi esterne, in particolare, hanno l'attributo ON DELETE CASCADE.
Sequenze
Qui abbiamo creato delle sequenze Oracle. Si tratta di generatori di numeri consecutivi. Ce ne sono 5 [1].
![]() |
- In [2], vediamo le proprietà della sequenza [SEQUENCE_CLIENTS]. Genera numeri consecutivi con incrementi di 1, a partire da 1 fino a un valore molto elevato.
Tutte le sequenze sono costruite secondo lo stesso modello.
- [SEQUENCE_CLIENTS] verrà utilizzata per generare la chiave primaria della tabella [CLIENTS];
- [SEQUENCE_DOCTORS] verrà utilizzata per generare la chiave primaria per la tabella [DOCTORS];
- [SEQUENCE_SLOTS] verrà utilizzata per generare la chiave primaria per la tabella [SLOTS];
- [SEQUENCE_RVS] verrà utilizzata per generare la chiave primaria per la tabella [RVS];
- [SEQUENCE_VERSIONS] verrà utilizzato per generare i valori delle colonne [VERSIONING] in tutte le tabelle.
Trigger
Un trigger è una procedura eseguita dal DBMS prima o dopo un evento (Insert, Update, Delete) in una tabella. Ne abbiamo 8 [1]:
![]() |
Diamo un'occhiata al codice DDL del trigger [TRIGGER_PK_CLIENTS], che popola la chiave primaria della tabella [CLIENTS]:
- Righe 1-5: prima di ogni operazione INSERT sulla tabella [CLIENTS];
- riga 6: la colonna [ID] assumerà il valore successivo della sequenza [SEQUENCE_CLIENTS]. La chiave primaria avrà quindi valori consecutivi forniti dalla sequenza.
I trigger [TRIGGER_PK_MEDECINS, TRIGGER_PK_CRENEAUX, TRIGGER_PK_RVS] funzionano in modo simile.
Diamo un'occhiata al codice DDL del trigger [TRIGGER_VERSIONS_CLIENTS], che popola la colonna [VERSIONING] della tabella [CLIENTS]:
- Righe 1-2: prima di ogni operazione INSERT o UPDATE sulla tabella [CLIENTS];
- riga 8: la colonna [VERSIONING] assumerà il valore successivo della sequenza [SEQUENCE_VERSIONS]. La colonna [VERSIONING] avrà quindi valori consecutivi forniti dalla sequenza.
I trigger [TRIGGER_VERSION_MEDECINS, TRIGGER_VERSION_CRENEAUX, TRIGGER_VERSION_RVS] funzionano in modo simile. Le quattro colonne [VERSIONING] traggono i propri valori dalla stessa sequenza.
Lo script per la generazione delle tabelle del database Oracle [RDVMEDECINS-EF] è stato inserito nella cartella [RdvMedecins / databases / oracle]. Il lettore può caricarlo ed eseguirlo per creare le tabelle.
Una volta fatto ciò, è possibile eseguire i vari programmi del progetto. Essi producono gli stessi risultati ottenuti con SQL Server, ad eccezione del programma [ModifyDetachedEntities], che va in crash per lo stesso motivo per cui andava in crash con MySQL. Il problema viene risolto allo stesso modo. È sufficiente copiare il programma [ModifyDetachedEntities] dal progetto [RdvMedecins-MySQL-01] nel progetto [RdvMedecins-Oracle-01]. Ciò presenta quindi un nuovo problema:
- righe 1–4: il client distaccato è stato aggiornato con successo;
- riga 6: un'eccezione nota. È quella che si ottiene quando si tenta di modificare un'entità senza disporre della versione corretta. Tuttavia, in questo caso, non volevamo modificare ma piuttosto eliminare l'entità:
// remove out-of-context entity
using (var context = new RdvMedecinsContext())
{
// here we have a new empty context
// put client1 in the context in a deleted state
context.Entry(client1).State = EntityState.Deleted;
// save the context
context.SaveChanges();
}
EF 5 ha rifiutato di eliminare client1 dal database perché client1 (riga 6) non aveva la stessa versione. Non avevamo riscontrato questo problema con MySQL. Ci stiamo gradualmente rendendo conto che i connettori ADO.NET per i diversi DBMS presentano lievi differenze. Lo correggiamo come segue:
using (var context = new RdvMedecinsContext())
{
// here we have a new empty context
// put client1 in the context to delete it
context.Clients.Remove(context.Clients.Find(client1.Id));
// save the context
context.SaveChanges();
}
e funziona.
5.3. Architettura multilivello basata su EF 5
Torniamo al nostro caso di studio descritto nel paragrafo 2 a pagina 7.
![]() |
Inizieremo creando il livello di accesso ai dati [DAO]. Per farlo, duplichiamo il progetto console VS 2012 [RdvMedecins-SqlServer-02] in [RdvMedecins-Oracle-02] [1]:
![]() |
- in [2], eliminiamo il progetto [RdvMedecins-SqlServer-02];
![]() |
- in [3], aggiungiamo un progetto esistente alla soluzione. Lo prendiamo dalla cartella [RdvMedecins-Oracle-02] appena creata;
- in [4], il nuovo progetto ha lo stesso nome di quello che è stato eliminato. Ne cambieremo il nome;
![]() |
- In [5], abbiamo modificato il nome del progetto;
- In [6], modifichiamo alcune delle sue proprietà, come ad esempio il nome dell'assieme;
- in [7], la cartella [Models] viene eliminata e sostituita dalla cartella [Models] del progetto [RdvMedecins-Oracle-01]. Questo perché i due progetti condividono gli stessi modelli.
![]() |
- in [8], i riferimenti attuali del progetto;
- in [9], è stato aggiunto il connettore Oracle ADO.NET utilizzando lo strumento NuGet.
Nel file [App.config], sostituire le informazioni relative al database SQL Server con quelle del database Oracle. Queste informazioni sono disponibili nel file [App.config] del progetto [RdvMedecins-Oracle-01]:
<!-- connection chain on base -->
<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>
<!-- the factory provider -->
<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>
Anche gli oggetti gestiti da Spring cambiano. Attualmente abbiamo:
<!-- spring configuration -->
<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>
La riga 7 fa riferimento all'assembly del progetto [RdvMedecins-SqlServer-02]. L'assembly è ora [RdvMedecins-Oracle-02].
Fatto ciò, siamo pronti per eseguire il test del livello [DAO]. Innanzitutto, dobbiamo assicurarci che il database sia popolato (utilizzando il programma [Fill] del progetto [RdvMedecins-Oracle-01]). Il programma di test viene eseguito con successo.
Creiamo la DLL del progetto come abbiamo fatto per il progetto [RdvMedecins-SqlServer-02] e raccogliamo tutte le DLL del progetto in una cartella [lib] creata all'interno di [RdvMedecins-Oracle-02]. Questi saranno i riferimenti per il progetto web [RdvMedecins-Oracle-03] che seguirà.
![]() |
Ora siamo pronti per sviluppare il livello [ASP.NET] della nostra applicazione:
![]() |
Inizieremo con il progetto [RdvMedecins-SqlServer-03]. Duplichiamo la cartella di questo progetto in [RdvMedecins-Oracle-03] [1]:
![]() |
- in [2], utilizzando VS 2012 Express for the Web, apriamo la soluzione nella cartella [RdvMedecins-Oracle-03];
- in [3], modifichiamo sia il nome della soluzione che quello del progetto;
![]() |
- in [4], i riferimenti attuali del progetto;
- in [5], li eliminiamo;
- in [6], per sostituirli con i riferimenti alle DLL che abbiamo appena salvato in una cartella [lib] all'interno del progetto [RdvMedecins-Oracle-02].
Non resta che modificare il file [Web.config]. Sostituiamo il suo contenuto attuale con il contenuto del file [App.config] del progetto [RdvMedecins-Oracle-02]. Una volta fatto questo, eseguiamo il progetto web. Funziona. Non dobbiamo dimenticare di popolare il database prima di eseguire l'applicazione web.




























