4. Caso di studio con MySQL 5.5.28
4.1. Installazione degli strumenti
Gli strumenti da installare sono i seguenti:
- SGBD: [http://dev.mysql.com/downloads/];
- uno strumento di amministrazione: EMS, SQL Manager per MySQL, Freeware [http://www.sqlmanager.net/fr/products/mysql/manager/download].
Negli esempi che seguono, l'utente root ha la password root.
Avviamo MySQL5. In questo caso, lo facciamo dalla finestra dei servizi di Windows [1]. In [2], viene avviato SGBD.
![]() |
Ora avviamo lo strumento [SQL Manager Lite for MySQL] con cui gestiremo il SGBD [3].
![]() |
- In [4], creiamo un nuovo database;
- in [5], si specifica il nome del database;
![]() |
- in [5], si effettua l'accesso come root / root;
- in [6], si conferma il comando SQL che verrà eseguito;
![]() |
- in [7], il database è stato creato. Ora deve essere salvato in [EMS Manager]. Le informazioni sono corrette. Si esegue [OK];
- in [8], ci si connette;
- in [9], [EMS Manager] visualizza il database, per ora vuoto.
Ora collegheremo un progetto VS 2012 a questo database.
4.2. Creazione del database a partire dalle entità
Creiamo il progetto console VS 2012 [RdvMedecins-MySQL-01] [1] riportato di seguito:
![]() |
- in [2], si aggiungono i riferimenti al progetto tramite NuGet;
![]() |
- in [3], si aggiunge il riferimento EF 5;
- in [4], ora è presente nei riferimenti;
![]() |
- in [5], si ricomincia per aggiungere questa volta [MySQL.Data.Entities], che è un connettore ADO.NET per Entity Framework. Per trovare il pacchetto, è possibile avvalersi della casella di ricerca [6];
- in [7] compaiono due riferimenti: [MySQL.Data.Entities] e [MySQL.Data], dove il secondo è una dipendenza del primo.
Ora si procederà alla creazione del progetto [RdvMedecins-MySQL-01] a partire dal progetto [RdvMedecins-SqlServer-01].
![]() |
- in [1], si copiano gli elementi selezionati;
- in [2], li incolliamo nel progetto [RdvMedecins-MySQL-01];
- in [3], poiché esistono diversi programmi con il metodo [Main], è necessario specificare il progetto di avvio.
A questo punto, la generazione del progetto dovrebbe andare a buon fine. Ora modificheremo il file di configurazione [App.config], che configura la stringa di connessione al database, e il file DbProviderFactory. Il risultato sarà il seguente:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- Per ulteriori informazioni sulla configurazione di Entity Framework, visitare http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
<!-- stringa di connessione-->
<connectionStrings>
<add name="monContexte"
connectionString="Server=localhost;Database=rdvmedecins-ef;Uid=root;Pwd=root;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
<!-- il provider di factory -->
<system.data>
<DbProviderFactories>
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"
/>
</DbProviderFactories>
</system.data>
</configuration>
- riga 17: la stringa di connessione al database MySQL [rdvmedecins-ef] che abbiamo creato;
- riga 24: la versione deve corrispondere a quella del riferimento [MySql.Data] del progetto [1]:
![]() |
C'è anche qualche impostazione nel file [Entites.cs], dove si specificano i nomi delle tabelle e lo schema a cui appartengono. Quest'ultimo può variare a seconda del SGBD. È il caso qui, dove non ci sarà alcuno schema. Il file [Entites.cs] si presenta come segue:
[Table("MEDECINS")]
public class Medecin : Personne
{...}
[Table("CLIENTS")]
public class Client : Personne
{...}
[Table("CRENEAUX")]
public class Creneau
{...}
[Table("RVS")]
public class Rv
{...}
Eseguiamo il programma [CreateDB_01] [2]. Si ottiene la seguente eccezione:
Lo stesso errore compare quattro volte (righe 2-5). Il tipo rowversion ricorda il campo con l’annotazione [Timestamp] nelle entità:
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
Decidiamo di sostituire queste tre righe con le seguenti:
[ConcurrencyCheck]
[Column("VERSIONING")]
public DateTime? Versioning { get; set; }
Modifichiamo il tipo della colonna da byte[] a DateTime?. Lo facciamo perché MySQL ha un tipo [TIMESTAMP] che rappresenta una data/ora e una colonna con questo tipo viene automaticamente aggiornata da MySQL ogni volta che la riga viene aggiornata. Questo ci consentirà di gestire gli accessi concorrenti.
L'annotazione [Timestamp] può essere applicata solo a una colonna di tipo byte[]. La sostituiamo con l'annotazione [ConcurrencyCheck]. Entrambe queste annotazioni gestiscono la concorrenza di accesso. Eseguiamo questa operazione per tutte e quattro le entità, quindi rieseguiamo l'applicazione. Otteniamo quindi il seguente errore:
La riga 1 indica un errore di sintassi nell’annotazione SQL eseguita da MySQL. Poiché quest’ultimo non è stato generato da noi ma dal provider ADO.NET di MySQL, non possiamo correggere questo punto. Tuttavia, si può notare che sono state create le tabelle [1] riportate di seguito:
![]() |
- in [2], si vede la struttura della tabella [clients] [3].
È necessario apportare diverse modifiche al database generato:
- il tipo della colonna [VERSIONING] non è corretto. È necessario assegnarle il tipo MySQL [TIMESTAMP];
- si ricorda che la tabella [rvs] presenta un vincolo di unicità. Non è stata creata da questa generazione;
- il connettore ADO.NET di SQL Server aveva generato chiavi esterne con la clausola ON DELETE CASCADE. Il connettore ADO.NET di MySQL non lo ha fatto.
Come abbiamo fatto con il server SQL, dobbiamo quindi modificare il database generato. Non mostriamo come apportare le modifiche. Forniamo semplicemente lo script per la creazione del database:
# SQL Manager Lite per MySQL 5.3.0.2
# ---------------------------------------
# Host : localhost
# Porta : 3306
# Database: rdvmedecins-ef
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
SET FOREIGN_KEY_CHECKS=0;
USE `rdvmedecins-ef`;
#
# Struttura della tabella `clients`:
#
CREATE TABLE `clients` (
`ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRENOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`TITRE` VARCHAR(5) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY USING BTREE (`ID`) COMMENT ''
)ENGINE=InnoDB
AUTO_INCREMENT=96 AVG_ROW_LENGTH=4096 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;
#
# Struttura della tabella `medecins`:
#
CREATE TABLE `medecins` (
`ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
`NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`PRENOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
`TITRE` VARCHAR(5) COLLATE utf8_general_ci NOT NULL,
`VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY USING BTREE (`ID`) COMMENT ''
)ENGINE=InnoDB
AUTO_INCREMENT=56 AVG_ROW_LENGTH=4096 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;
#
# Struttura della tabella `creneaux`:
#
CREATE TABLE `creneaux` (
`ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
`HDEBUT` INTEGER(11) NOT NULL,
`MDEBUT` INTEGER(11) NOT NULL,
`HFIN` INTEGER(11) NOT NULL,
`MFIN` INTEGER(11) NOT NULL,
`MEDECIN_ID` INTEGER(11) NOT NULL,
`VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY USING BTREE (`ID`) COMMENT '',
INDEX `MEDECIN_ID` USING BTREE (`MEDECIN_ID`) COMMENT '',
CONSTRAINT `creneaux_ibfk_1` FOREIGN KEY (`MEDECIN_ID`) REFERENCES `medecins` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
)ENGINE=InnoDB
AUTO_INCREMENT=472 AVG_ROW_LENGTH=455 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;
#
# Struttura della tabella `rvs`:
#
CREATE TABLE `rvs` (
`ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
`JOUR` DATE NOT NULL,
`CRENEAU_ID` INTEGER(11) NOT NULL,
`CLIENT_ID` INTEGER(11) NOT NULL,
`VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY USING BTREE (`ID`) COMMENT '',
UNIQUE INDEX `CRENEAU_ID_JOUR` USING BTREE (`JOUR`, `CRENEAU_ID`) COMMENT '',
INDEX `CRENEAU_ID` USING BTREE (`CRENEAU_ID`) COMMENT '',
INDEX `CLIENT_ID` USING BTREE (`CLIENT_ID`) COMMENT '',
CONSTRAINT `rvs_ibfk_2` FOREIGN KEY (`CLIENT_ID`) REFERENCES `clients` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `rvs_ibfk_1` FOREIGN KEY (`CRENEAU_ID`) REFERENCES `creneaux` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
)ENGINE=InnoDB
AUTO_INCREMENT=28 AVG_ROW_LENGTH=16384 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;
- righe 22, 38, 54, 74: le chiavi primarie ID delle tabelle sono di tipo AUTO_INCREMENT, quindi generate da MySQL;
- righe 26, 42, 60, 78: la colonna VERSIONING è di tipo TIMESTAMP e viene aggiornata durante un'operazione INSERT o UPDATE;
- riga 63: la chiave esterna dalla tabella [creneaux] alla tabella [medecins] con la clausola ON DELETE CASCADE;
- riga 80: il vincolo di unicità della tabella [rvs];
- riga 83: la chiave esterna della tabella [rvs] verso la tabella [creneaux] con la clausola ON DELETE CASCADE;
- riga 84: la chiave esterna della tabella [rvs] verso la tabella [clients] con la clausola ON DELETE CASCADE;
Lo script per la generazione delle tabelle del database MySQL [rvmedecins-ef] è stato inserito nella cartella [RdvMedecins / databases / mysql]. Il lettore potrà caricarlo ed eseguirlo per creare le proprie tabelle.
Una volta fatto ciò, è possibile eseguire i vari programmi del progetto. Essi forniscono gli stessi risultati ottenuti con SQL Server, ad eccezione del programma [ModifyDetachedEntities] che va in crash. Per capirne il motivo, si può osservare il risultato del programma [ModifyAtttachedEntities]:
- righe 1-2: un cliente prima del salvataggio del contesto;
- righe 3-4: il client dopo il salvataggio. Ha una chiave primaria ma nessun valore per il campo [Versioning], mentre il programma SQL Server aggiornava il campo [Timestamp] dell’entità.
Ora esaminiamo il codice del programma [ModifyDetachedEntities] che va in crash:
using System;
...
namespace RdvMedecins_01
{
class ModifyDetachedEntities
{
static void Main(string[] args)
{
Client client1;
// si svuota il database attuale
Erase();
// si aggiunge un cliente
using (var context = new RdvMedecinsContext())
{
// Creazione cliente
client1 = new Client { Titre = "x", Nom = "x", Prenom = "x" };
// aggiunta del cliente al contesto
context.Clients.Add(client1);
// salvataggio del contesto
context.SaveChanges();
}
// visualizzazione di base
Dump("1-----------------------------");
// il cliente1 non è presente nel contesto - lo si modifica
client1.Nom = "y";
// modifica entità fuori contesto
using (var context = new RdvMedecinsContext())
{
// qui si ha un nuovo contesto vuoto
// si inserisce client1 nel contesto in uno stato modificato
context.Entry(client1).State = EntityState.Modified;
// si salva il contesto
context.SaveChanges();
}
...
}
static void Erase()
{
...
}
static void Dump(string str)
{
...
}
}
}
- riga 20: viene salvato un cliente. A questo punto ha la sua chiave primaria, ma in base alla sua versione;
- riga 33: viene apportata una modifica con client1. L'operazione fallisce perché non ha la versione presente nel database.
Si risolve il problema inserendo il seguente codice tra le righe 25 e 26:
// si recupera client1 per ottenere la sua versione
using (var context = new RdvMedecinsContext())
{
// client2 sarà nel contesto
Client client2 = context.Clients.Find(client1.Id);
// si imposta la versione di client1 su quella di client2
client1.Versioning = client2.Versioning;
}
Ora l’entità [client1] ha la stessa versione presente nel database e può quindi essere utilizzata per aggiornare la riga nel database.
4.3. Architettura multistrato basata su EF 5
Torniamo al nostro caso di studio descritto al paragrafo 2.
![]() |
Inizieremo con la creazione del livello di accesso ai dati [DAO]. A tal fine, creiamo il progetto console VS 2012 [RdvMedecins-MySQL-02] [1]:
![]() |
- in [2], i riferimenti [Common.Logging, EntityFramework, MySql.Data, MySql.Data.Entity, Spring.Core] vengono aggiunti insieme a NuGet;
- in [3], la cartella [Models] viene copiata dal progetto [RdvMedecins-MySQL-01];
![]() |
- in [4], le cartelle [Dao, Exception, Tests] e il file [App.config] vengono copiati dal progetto [RdvMedecins-SqlServer-02];
- in [5], il file [Program.cs] è stato eliminato;
- nel progetto [6], il progetto è configurato per eseguire il programma di test del livello [DAO].
Nel file [App.config], le informazioni del database SQL Server vengono sostituite con quelle del database MySQL. Queste informazioni si trovano nel file [App.config] del progetto [RdvMedecins-MySQL-01]:
<!-- stringa di connessione-->
<connectionStrings>
<add name="monContexte"
connectionString="Server=localhost;Database=rdvmedecins-ef;Uid=root;Pwd=root;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
<!-- il provider factory -->
<system.data>
<DbProviderFactories>
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"
/>
</DbProviderFactories>
</system.data>
Anche gli oggetti gestiti da Spring cambiano. Attualmente abbiamo:
<!-- configurazione 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>
La riga 7 fa riferimento all’assembly del progetto [RdvMedecins-SqlServer-02]. L’assembly è ora [RdvMedecins-MySQL-02].
Fatto ciò, siamo pronti per eseguire il test del livello [DAO]. Prima è necessario assicurarsi di popolare il database (programma [Fill] del progetto [RdvMedecins-MySQL-01]). Il programma di test va a buon fine.
Creiamo il file DLL del progetto come è stato fatto per il progetto [RdvMedecins-SqlServer-02] e raggruppiamo l’tutti i file DLL del progetto in una cartella [lib] creata all’interno di [RdvMedecins-MySQL-02]. Questi saranno i riferimenti del progetto web [RdvMedecins-MySQL-03] che seguirà.
![]() |
Ora siamo pronti per costruire il livello [ASP.NET] della nostra applicazione:
![]() |
Partiremo dal progetto [RdvMedecins-SqlServer-03]. Duplichiamo la cartella di questo progetto in [RdvMedecins-MySQL-03] e [1]:
![]() |
- in [2], con VS 2012 Express per il web, apriamo la soluzione della cartella [RdvMedecins-MySQL-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 riferimenti a DLL che abbiamo appena salvato in una cartella [lib] del progetto [RdvMedecins-MySQL-02].
Non ci resta che modificare il file [Web.config]. Sostituiamo il suo contenuto attuale con quello del file [App.config] del progetto [RdvMedecins-MySQL-02]. Fatto ciò, eseguiamo il progetto web. Funziona.
4.4. Conclusion
Riassumiamo cosa è stato fatto per passare dal server SGBD SQL al server SGBD MySQL:
- il campo utilizzato per gestire la concorrenza di accesso alle entità è stato modificato. La sua versione sul server SQL era:
[Column("TIMESTAMP")]
[Timestamp]
public byte[] Timestamp { get; set; }
È diventata:
[ConcurrencyCheck]
[Column("VERSIONING")]
public DateTime? Versioning { get; set; }
con MySQL;
- le annotazioni [Table] che collegano un'entità a una tabella sono state modificate;
- la stringa di connessione al database e il [DbProviderFactory] sono stati modificati nei file di configurazione [App.config] e [Web.config];
- dopo il salvataggio nel database, un’entità SQL Server presentava sia la chiave primaria che il Timestamp. Con MySQL, presentava solo la chiave primaria. Ciò ha comportato la modifica di una parte di codice.
In definitiva, si è trattato di modifiche piuttosto limitate, ma è stato comunque necessario rivedere il codice. Ripetiamo la stessa procedura per altri tre SGBD:
- Il SGBD Oracle Database Express Edition 11g Release 2;
- Il SGBD PostgreSQL 9.2.1;
- SGBD Firebird 2.1.
















