6. Caso di studio con PostgreSQL 9.2.1
6.1. Installazione degli strumenti
Gli strumenti da installare sono i seguenti:
- il DBMS: [http://www.enterprisedb.com/products-services-training/pgdownload#windows];
- uno strumento di amministrazione: EMS SQL Manager for PostgreSQL Freeware [http://www.sqlmanager.net/fr/products/postgresql/manager/download].
Negli esempi seguenti, l'utente postgres ha la password postgres.
Avviamo PostgreSQL e poi lo strumento [SQL Manager Lite for PostgreSQL], che useremo per amministrare il DBMS.
![]() |
- In [1], avviamo il DBMS PostgreSQL da Servizi di Windows;
- In [2], il servizio è avviato;
Ora avviamo lo strumento [SQL Manager Lite for MySQL], che useremo per amministrare il DBMS [3].
![]() |
- In [4], creiamo un nuovo database;
- In [5], specifichiamo il nome del database;
![]() |
- In [5], effettuiamo l'accesso come postgres / postgres;
- In [6] forniamo alcune informazioni;
- In [7], convalidiamo l'istruzione SQL da eseguire;
![]() |
- In [8], il database è stato creato. Ora deve essere registrato in [EMS Manager]. Le informazioni sono corrette. Fare clic su [OK];
- In [9], connettersi ad esso;
- In [10], [EMS Manager] visualizza il database, che al momento è vuoto. Si noti che le tabelle apparterranno a uno schema denominato public [11].
Ora collegheremo un progetto VS 2012 a questo database.
6.2. Creazione del database dalle entità
Iniziamo duplicando la cartella del progetto [RdvMedecins-SqlServer-01] in [RdvMedecins-PostgreSQL-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-PostgreSQL-01] che abbiamo creato in precedenza;
![]() |
- in [5], il progetto caricato si chiama [RdvMedecins-SqlServer-01];
- in [6], lo rinominiamo [RdvMedecins-PostgreSQL-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-PostgreSQL-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="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- the 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>
- riga 3: nome utente e password;
- Righe 7–9: il DbProviderFactory. La riga 8 fa riferimento a una DLL [Npgsql] che non abbiamo. Possiamo ottenerla utilizzando NuGet [1]:
![]() |
- in [2], digitare la parola chiave postgresql nella casella di ricerca;
- in [3], selezionare il pacchetto [Npgsql]. Si tratta di un connettore ADO.NET per PostgreSQL;
![]() |
- in [4], sono stati aggiunti due riferimenti;
- 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:
[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
{...}
Abbiamo visto in precedenza, durante la creazione di un database PostgreSQL, che le tabelle appartenevano a uno schema denominato public.
Configuriamo l'esecuzione 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 e Oracle. Ciò è legato al tipo del campo Timestamp nelle entità. Apportiamo la stessa modifica utilizzata con Oracle. 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; }
Modifichiamo il tipo della colonna da byte[] a int?. Nel DBMS, utilizzeremo procedure memorizzate per incrementare questo numero intero di uno 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 ADO.NET di PostgreSQL non è in grado di eliminare il database esistente. Proprio come con Oracle. Siamo quindi tenuti a creare manualmente il database [RDVMEDECINS-EF] utilizzando lo strumento [EMS Manager for PostgreSQL]. Non descriveremo ogni passaggio, ma solo quelli più importanti.
Il database PostgreSQL sarà il seguente:
Le tabelle
![]() |
- In [1], ID è una chiave primaria di tipo serial. Questo tipo di PostgreSQL è un numero intero generato automaticamente dal DBMS.
![]() |
![]() |
![]() |
![]() |
Le varie tabelle contengono le chiavi primarie e le chiavi esterne presenti nelle tabelle degli esempi precedenti. Le chiavi esterne hanno l'attributo ON DELETE CASCADE.
Sequenze
Come in Oracle, anche qui abbiamo creato delle sequenze. Si tratta di generatori di numeri consecutivi. Ce ne sono 5 [1].
![]() |
- In [2] vediamo le proprietà della sequenza [CLIENTS_ID_SEQ]. Genera numeri consecutivi con incrementi di 1, a partire da 1 fino a un valore molto elevato.
Tutte le sequenze sono costruite sullo stesso modello.
- [CLIENTS_ID_seq] verrà utilizzata per generare la chiave primaria della tabella [CLIENTS];
- [MEDECINS_ID_seq] verrà utilizzata per generare la chiave primaria della tabella [MEDECINS];
- [SLOTS_ID_seq] verrà utilizzato per generare la chiave primaria per la tabella [SLOTS];
- [RVS_ID_seq] verrà utilizzato 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 4 [1]:
![]() |
Diamo un'occhiata al codice DDL per il trigger [CLIENTS_tr], che popola la colonna [VERSIONING] della tabella [CLIENTS]:
- righe 1-3: prima di ogni operazione INSERT o UPDATE sulla tabella [CLIENTS];
- Riga 4: viene eseguita la procedura [public.trigger_versions()].
La procedura [public.trigger_versions()] è la seguente:
- Riga 2: NEW rappresenta la riga che sta per essere inserita o modificata. NEW. "VERSIONING" è la colonna [VERSIONING] di quella riga. Le viene assegnato il valore successivo generato dal generatore di numeri "sequence_versions". Pertanto, la colonna ["VERSIONING"] cambia ad ogni INSERT o UPDATE eseguito sulla tabella [CLIENTS].
I trigger [MEDECINS_tr, CRENEAUX_tr, RVS_tr] funzionano allo stesso modo. Le quattro colonne ["VERSIONING"] traggono i propri valori dalla stessa sequenza.
Lo script per la generazione delle tabelle nel database PostgreSQL [RDVMEDECINS-EF] è stato inserito nella cartella [RdvMedecins / databases / postgreSQL]. 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 Oracle. Il problema viene risolto allo stesso modo. È sufficiente copiare il programma [ModifyDetachedEntities] dal progetto [RdvMedecins-Oracle-01] nel progetto [RdvMedecins-PostgreSQL-01].
Il programma [LazyEagerLoading] va in crash con la seguente eccezione:
Il codice errato è il seguente:
using (var context = new RdvMedecinsContext())
{
// crenel n° 0
creneau = context.Creneaux.Include("Medecin").Single<Creneau>(c => c.Id == idCreneau);
Console.WriteLine(creneau.ShortIdentity());
}
Riga 1 dell'eccezione: l'errore segnalato suggerisce un join poiché LEFT è una parola chiave di join. Poiché la riga 4 del codice sopra riportato richiede il caricamento immediato della dipendenza [Doctor] per un'entità [Appointment], EF ha eseguito un join tra le tabelle [APPOINTMENTS] e [DOCTORS]. Tuttavia, sembra che il connettore ADO.NET abbia generato un'istruzione SQL errata. Riscriviamo il codice come segue:
using (var context = new RdvMedecinsContext())
{
// crenel n° 0
creneau = context.Creneaux.Find(idCreneau);
Console.WriteLine(creneau.ShortIdentity());
// force the loading of the associated doctor
// it's possible because we're still in an open context
Medecin medecin = creneau.Medecin;
}
- riga 4: recuperiamo lo slot senza un join;
- riga 8: recuperiamo la dipendenza mancante.
Funziona. Ancora una volta, vediamo che cambiare il DBMS ha un impatto sul codice. In realtà, il problema qui non è il DBMS, ma il suo connettore ADO.NET.
6.3. Architettura multilivello basata su EF 5
Torniamo al nostro caso di studio descritto nel paragrafo 2.
![]() |
Inizieremo creando il livello di accesso ai dati [DAO]. A tal fine, duplichiamo il progetto console VS 2012 [RdvMedecins-SqlServer-02] in [RdvMedecins-PostgreSQL-02] [1]:
![]() |
- In [2], eliminare il progetto [RdvMedecins-SqlServer-02];
![]() |
- In [3], aggiungere un progetto esistente alla soluzione. Prenderlo dalla cartella [RdvMedecins-PostgreSQL-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 il nome dell'assieme;
- in [7], la cartella [Models] viene eliminata e sostituita dalla cartella [Models] del progetto [RdvMedecins-PostgreSQL-01]. Questo perché entrambi i progetti condividono gli stessi modelli.
![]() |
- in [8], i riferimenti attuali del progetto;
- in [9], il connettore PostgreSQL ADO.NET è stato aggiunto utilizzando lo strumento NuGet.
Nel file [App.config], sostituire le informazioni relative al database SQL Server con quelle del database PostgreSQL. Queste informazioni sono disponibili nel file [App.config] del progetto [RdvMedecins-PostgreSQL-01]:
<!-- connection chain on base -->
<connectionStrings>
<add name="monContexte" connectionString="Server=127.0.0.1;Port=5432;Database=rdvmedecins-ef;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<!-- the 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>
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-PostgreSQL-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-PostgreSQL-01]). Il programma di test va in crash con la seguente eccezione:
Riga 13: Il messaggio indica che l'errore si è verificato nel metodo [GetCreneauxMedecin] del livello [DAO]. Questo metodo è il seguente:
// list of time slots for a given doctor
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// list of slots
try
{
// opening persistence context
using (var context = new RdvMedecinsContext())
{
// we get the doctor back with his slots
Medecin medecin = context.Medecins.Include("Creneaux").Single(m => m.Id == idMedecin);
// returns a list of the doctor's slots
return medecin.Creneaux.ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
Riga 11: Riconosciamo la parola chiave Include, che in precedenza causava il crash del programma. Il codice precedente può essere sostituito con il seguente:
// list of time slots for a given doctor
public List<Creneau> GetCreneauxMedecin(int idMedecin)
{
// list of slots
try
{
// opening persistence context
using (var context = new RdvMedecinsContext())
{
// returns a list of the doctor's slots
return context.Creneaux.Where(c => c.MedecinId == idMedecin).ToList<Creneau>();
}
}
catch (Exception ex)
{
throw new RdvMedecinsException(3, "GetCreneauxMedecin", ex);
}
}
Il nuovo codice sembra persino più coerente di quello vecchio. In ogni caso, questa volta il programma di test viene superato.
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-PostgreSQL-02]. Questi saranno i riferimenti per il progetto web [RdvMedecins-PostgreSQL-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-PostgreSQL-03] [1]:
![]() |
- in [2], utilizzando VS 2012 Express for the Web, apriamo la soluzione nella cartella [RdvMedecins-PostgreSQL-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] nel progetto [RdvMedecins-PostgreSQL-02].
Non resta che modificare il file [Web.config]. Sostituiamo il suo contenuto attuale con il contenuto del file [App.config] del progetto [RdvMedecins-PostgreSQL-02]. Una volta fatto questo, eseguiamo il progetto web. Funziona. Non dobbiamo dimenticare di popolare il database prima di eseguire l'applicazione web.


























