Le nuove potenzialità di EF6 consentono di creare un sito web e relativo data model con poche righe di codice. Sul web esistono numerose guide che illustrano come fare utilizzando SQL Express, ma chi sceglie di utilizzare MySQL troverà senz'altro molto meno materiale. Questa semplice guida risponde a quanti mi hanno chiesto di riassumere i passaggi fondamentali per creare una applicazione web MVC5 configurando EF6 CF con MySQL. Per i primi passaggi mi limiterò a tradurre l'ottimo articolo Getting started with Entity Framework 6 Code First using MVC5 presente sul sito www.asp.net, dal quale prenderò anche alcune immagini a scopo divulgativo.
Questi gli strumenti di cui avremo bisogno (e relativi link alle versioni scaricabili gratuitamente):
- Visual Studio 2013, scaricabile qui in versione prova per 90 giorni.
- .NET Framework 4.5 o superiore, scaricabile qui.
- Entity Framework 6 o superiore, scaricabile tramite NuGet.
- MySQL Server 5.6 o superiore, scaricabile dal sito ufficiale, o accesso ad un database MySQL su un server remoto.
- MySQL Connector.NET 6.9.4 o superiore, scaricabile dal sito ufficiale oppure tramite NuGet.
Step 1. Creazione di una Web Application
Apriamo Visual Studio 2013 e creiamo un nuovo progetto C# in questo modo:
Nella schermata successiva selezioniamo il template MVC, quindi clicchiamo sul bottone Change Authentication... per decidere se abilitare o meno l'autenticazione utente a seconda delle necessità del sito che vogliamo creare. Nell'esempio che vogliamo fare non ne abbiamo bisogno, quindi possiamo scegliere No Authentication.
Completiamo quindi i passaggi necessari per ultimare la creazione del sito.
Step 2. Installazione di Entity Framework 6
Dal menu Tools (Strumenti, se avete la versione in italiano) selezioniamo Library Package Manager e poi Package Manager Console. Nella console che si aprirà digitiamo il comando seguente:
> Install-Package EntityFramework
NuGet si occuperà di installare la versione più recente di Entity Framework (al momento è la 6.1.1). Una volta fatto questo possiamo cominciare a creare le nostre Entities seguendo l'approccio code-first.
Step 3. Creazione delle Entities
Una Entity non è altro che una classe pensata per contenere tutte le informazioni relative a un singolo elemento del nostro Database: prima di creare una o più Entities è quindi necessario avere una idea piuttosto chiara degli elementi di cui avremo bisogno e delle relazioni tra gli stessi che andremo ad impostare. Prendiamo come esempio il più classico degli archivi:
- un elenco di studenti (Student)
- che effettuano una o più iscrizioni (Enrollment)
- a un elenco di corsi (Course).
Spostiamoci quindi all'interno della directory /Models/ (creandola se non esiste) e creiamo le seguenti tre classi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using System; using System.Collections.Generic; namespace MyApplication.Models { public class Student { public int ID { get; set; } public string LastName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } } |
1 2 3 4 5 6 7 8 9 10 |
using System; namespace MyApplication.Models { public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } </code> public virtual Course Course { get; set; } public virtual Student Student { get; set; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace MyApplication.Models { public class Course { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } } |
Step 4. Creazione del Database Context
La classe principale che si occupa di tenere insieme le funzionalità dell'Entity Framework è nota come Database Context. Per crearla è sufficiente estendere la classe di sistema System.Data.Entity.DbContext specificando le Entities da includere nel Data Model. Creiamo quindi una cartella /DAL/ (che sta per Data Access Layer) e aggiungiamo le due classi seguenti:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using MyApplication.Models; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; namespace MyApplication.DAL { public class MyDbContext : DbContext { public MyDbContext() : base("MyDbContextConnectionString") { Database.SetInitializer<MyDbContext>(new MyDbInitializer()); } public DbSet<Student> Students { get; set; } public DbSet<Enrollment> Enrollments { get; set; } public DbSet<Course> Courses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } } } |
1 2 3 4 5 6 7 8 9 10 11 |
public class MyDbInitializer : CreateDatabaseIfNotExists<MyDbContext> { protected override void Seed(MyDbContext context) { // create 3 students to seed the database context.Students.Add(new Student { ID = 1, FirstName = "Mark", LastName = "Richards", EnrollmentDate = DateTime.Now }); context.Students.Add(new Student { ID = 2, FirstName = "Paula", LastName = "Allen", EnrollmentDate = DateTime.Now }); context.Students.Add(new Student { ID = 3, FirstName = "Tom", LastName = "Hoover", EnrollmentDate = DateTime.Now }); base.Seed(context); } } |
Come si può vedere la classe MyDbContext.cs contiene un DbSet per ogni Entity creata in precedenza. Questo perché, secondo quanto previsto da Entity Framework, a ciascun DbSet corrisponderà una Tabella del nostro Database: le Entities, ovviamente, rappresenteranno le singole righe. Notiamo anche che nel costruttore viene impostato un oggetto di tipo MyDbInitializer, specificato nella classe successiva, che serve a creare il database nel caso in cui non esista e ad inserire tre record di esempio nella tabella Student che verrà creata. E' possibile derivare il MyDbInitializer da numerose possibili classi di inizializzazione predefinite, come ad esempio:
- CreateDatabaseIfNotExists: E' l'initializer predefinito: come suggerisce il nome, crea il database se questo non esiste: nel caso in cui il modello venga cambiato in modo da non coincidere più con il database esistente questo inizialier lancerà una eccezione. Questo comportamento lo rende ideale per gli ambienti di produzione, meno per gli ambienti di sviluppo dove il Database Model cambia di frequente.
- DropCreateDatabaseIfModelChanges: Questo initializer crea un nuovo database, eliminando quello eventualmente già esistente, se una o più classi relative alle Entities risultano modificate rispetto all'ultima inizializzazione. Questo comportamento lo rende ideale per gli ambienti di sviluppo, dove non è solitamente importante mantenere i dati nel DB: è sconsigliato per gli ambienti di produzione in quanto il rischio di data loss è elevato (basta un minimo aggiornamento a una classe Entity per perdere tutto l'archivio).
- DropCreateDatabaseAlways: Come suggerisce il nome, questo initializer elimina e ricrea il database ad ogni inizializzazione, ovvero ad ogni avvio dell'applicazione web. Il suo comportamento lo rende ideale per quegli ambienti di sviluppo che necessitano di un refresh continuo dei dati nel database: è ovviamente sconsigliato per gli ambienti di produzione.
Notiamo infine che il costruttore della nostra classe contiene un riferimento a una MyDbContextConnectionString che avremo cura di definire nel nostro Web.Config nel corso del prossimo step.
Step 5. Collegamento con MySQL
Per collegare la nostra web application al database MySQ abbiamo bisogno del MySQL Connector.NET, scaricabile gratuitamente dal sito web ufficiale oppure tramite NuGet. Una volta collegate le librerie al progetto avremo cura di aggiungere al Web.Config della nostra applicazione una stringa di connessione valida, completa di username e password, in questo modo:
1 2 3 |
<connectionStrings> <add name="MyDbContextConnectionString" providerName="MySql.Data.MySqlClient" connectionString="server=localhost;UserId=username;Password=password;database=myDatabase;CharSet=utf8;Persist Security Info=True"/> </connectionStrings> |
Step 6. Avvio dell'applicazione e creazione del Database
Se tutti i passaggi sono stati eseguiti correttamente, l'applicazione provvederà a creare automaticamente il database non appena verrà istanziato un oggetto di tipo MyDbContext.
1 |
private MyDbContext db = new MyDbContext(); |
Questo tipo di inizializzazione può avvenire all'interno di un qualsiasi Controller oppure nel Global.asax sotto forma di proprietà statica centralizzata o in qualsiasi altro punto dell'applicazione, a seconda delle necessità dello sviluppatore e/o del design pattern e/o di eventuali strategie di IoC / UoW adottate. Nel corso di articoli successivi provvederò a fornire esempi più su queste tecniche, sull'utilizzo delle Data Migrations e sulle molte altre potenzialità dell'Entity Framework.
Eccellente articolo. In relazione ad esso, ed alla stringa di connessione al DB MySql, volevo sapere se è possibile, come nel caso di un DB Sql Server, utilizzare la keyword “AttachDbFilename” per fare in modo di avere il DB all’interno della cartella “App_Data” del Progetto MVC (e.g.: AttachDbFilename=|DataDirectory|ContosoUniversity.mdf;).
Grazie.
Francesco.
Ciao e grazie!
Purtroppo no: la keyword AttachDbFilename è una caratteristica specifica di LocalDB, una versione ultraleggera di SQL Server che consente di creare e interagire direttamente con DB presenti sul filesystem. MySQL al momento non dà questa possibilità, l’unico modo per accedere ai file MyISAM o InnoDB è tramite il servizio MySQL mediante chiamate TCP/UDP (su 3306 o altra porta custom) o Socket.
Per maggiori informazioni su LocalDB ti rimando al seguente articolo della KB:
http://msdn.microsoft.com/en-us/library/hh309441%28v=vs.110%29.aspx
Qui invece trovi un recente articolo (in inglese) che spiega alcune delle insidie legate all’utilizzo di AttachDbFilename:
http://blogs.sqlsentry.com/aaronbertrand/bad-habits-attachdbfilename/
Anche io, come l’autore del post, consiglio di utilizzare una istanza reale di SQL Server quando possibile, limitando l’utilizzo di LocalDB a quegli scenari in cui non c’è modo di connettersi a un DB reale neppure in produzione (es. il classico dominio Aruba acquistato senza DB a parte).
Chiaro ed esaustivo.
Grazie mille!!!
Francesco.
Semplice e lineare. Compliementi per la qualità dell’esposizione.
Semplice e lineare. Complimenti per la qualità dell’articolo.