posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

EF Code First Migrations Beta 1 (Parte 1)

 

Finalmente sono riuscito a trovare qualche minuto per buttare giù qualche riga a riguardo di EF Code First Migrations (da qualche giorno disponibile in  beta 1). Per chi usa l’approccio Code First l’aggiornamento del modello e della base di dati sottostante (soprattutto quando contiene dati)  è un grosso problema. Proviamo a testare il funzionamento del “pacchetto” su un modello molto semplice come il seguente, in un progetto console C#:

 public class OfficeContext : DbContext
    {
        public OfficeContext()
            : base("OfficeDB")
        {
        }
        public DbSet<Employee> Employees { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
        static OfficeContext()
        {
            Database.SetInitializer<OfficeContext>(null);
        }
    }

    public class Employee
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public String Surnamte { get; set; }
        public String Role { get; set; }
    }

Aggiungiamo al progetto un file App.Config con una stringa di connessione simile alla seguente:

<add name="OfficeDB" connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=OfficeDB;Integrated Security=True;" providerName="System.Data.SqlClient"/>

Per come abbiamo configurato il DBContext nessun database verrà generato, anzi, a runtime avremo un’eccezione di questo tipo:

image

Procediamo con l’installazione via NuGet del Package EntityFramework.Migrations (Code First Migrations Beta 1, ver. 0.8.0.0):

image

Durante il processo di installazione viene automaticamente aggiunta al progetto una nuova cartella, “Migrations” contenente il file Configuration.cs (derivata da DbMigrationsConfiguration<T> dove T è la classe OfficeContext) nel quale possiamo specificare, ad esempio, se utilizzare la “migrazione automatica del modello” (vedremo in seguito) tramite la proprietà AutomaticMigrationsEnabled (false di default), se continuare o meno una migrazione dello schema anche in presenza di perdita dei dati, AutomaticMigrationDataLossAllowed, o se sollevare un’eccezione. Inoltre tramite l’override di Seed è possibile specificare se aggiungere dopo la generazione\migrazione dello schema, dei dati al database (avendo avuto qualche problema nei test con l’estensione AddOrUpdate preferisco utilizzare codice SQL per il momento Sorriso) . La classe Configuration sarà così definita:

 internal sealed class Configuration : DbMigrationsConfiguration<OfficeContext>
    {
        protected override void Seed(OfficeContext context)
        {
            base.Seed(context);
        }
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            AutomaticMigrationDataLossAllowed = false;
        }
    }

Ora, per eseguire la creazione del database, dobbiamo aggiungere “a scaffale” la nostra prima “migrazione”(ovvero la generazione del database): così facendo, oltre a richiamare i metodi per la generazione del database, potremmo eseguire nel tempo degli Upgrade e Downgrade utilizzando i vari piani di migrazione che andremo a creare: se uniamo a quanto detto TFS, penso non ci sia altro da aggiungere. Nella console di NuGet (Package Manager Console) digitiamo : Add-Migration [parametro] dove parametro è un etichetta che identifica in modo univoco la migrazione che stiamo aggiungendo. Nel nostro caso parametro=OfficeDBFirstMigration.  Se tutto procede senza errori, la schermata della console di NuGet dovrebbe essere simile alla seguente:

image

Al progetto viene aggiunto un un file xxxx_OfficeDBFirstMigration.cs contenente uno “Snapshot” del modello Code First del database da creare definito dalla classe DbContext (OfficeContext per il caso specifico). Per generare il database, digitiamo il comando Update-Database (che prende in considerazione l’ultimo file di migrazione “Pending”,  “a scaffale”), eventualmente aggiungendo il parametro –Verbose per visualizzare i comandi SQL generati, oppure il parametro –Script per generare un file .sql da scambiare con gli altri membri del team o eventualmente da aggiungere su TFS:

image

Utilizzando SQL Management Studio possiamo vedere come il database sia stato creato secondo le nostre specifiche (le colonne sono state aggiunge utilizzando le convenzioni di EF):

image

Prima di continuare, facciamo un passo indietro e  “spulciamo” il contenuto della classe OfficeDBFirstMigration generata dal comando Add-Migration:

public partial class OfficeDBFirstMigration : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "Employees",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    Surname = c.String(),
                    Role = c.String(),
                })
            .PrimaryKey(t => t.Id);
        CreateTable(
            "EdmMetadata",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    ModelHash = c.String(),
                })
            .PrimaryKey(t => t.Id);
        Sql("INSERT INTO Employees ([Name],[Surname],[Role]) VALUES ('Pietro','Libro','Art Director')");
        Sql("INSERT INTO Employees ([Name],[Surname],[Role]) VALUES ('Mario','Rossi','Administrator')");
        Sql("INSERT INTO Employees ([Name],[Surname],[Role]) VALUES ('Giulio','Verdi','Store Manager')");
    }
    public override void Down()
    {
        DropTable("EdmMetadata");
        DropTable("Employees");
    }
}

Nella classe è stato generato l’override dei metodi Up e Down (sembra il motivo di una canzone di qualche anno fa Sorriso ) . Up, come è facile intuire, contiene il codice eseguito per l’esecuzione dell’upgrade dello schema della base dati, Down, il codice eseguito per effettuare  il downgrade ad una versione precedente della base dati. La lettura del codice è abbastanza semplice, ed in questo punto possiamo intervenire per specificare manualmente (ove necessario) quanto non automaticamente generato da VS: ad esempio avremmo potuto aggiungere del codice per specificare degli indici  (automatico per le chiavi primarie, Id nello specifico) o delle Foreign Key. Inoltre possiamo aggiungere del codice SQL personalizzato utilizzando il metodo SQL (…). Utilizziamo questo metodo per aggiungere tre righe alla tabella Employees.

Eseguendo il nostro progetto , il risultato che otteniamo è mostrato figura:

image

Modifichiamo il modello dati, nello specifico l’entità Employee: aggiungiamo la colonna CardID (di tipo stringa) contenente il numero identificativo stampato sul badge di ogni dipendente, ed impostiamo tramite override dell’OnModelBuilder la lunghezza massima del campo a 20 caratteri. Inizialmente tutti i dipendenti dovranno avere un identificativo del tipo “00000-00000”. Procediamo con il comando Add-Migration OfficeDBSecondMigration, per aggiungere un nuovo piano di migrazione, ovvero un nuovo file .cs del tipo xxxxxx_OfficeDBSecondMigration.cs, che andiamo immediatamente ad ispezionare per aggiungere del codice personalizzato (per impostare il default value) prima di procedere con l’aggiornamento:

public partial class OfficeDBsecondMigration : DbMigration
{
    public override void Up()
    {
        AddColumn("Employees", "CardID", c => c.String(nullable: false, defaultValue: "00000-00000", maxLength:20));
    }
    public override void Down()
    {
        DropColumn("Employees", "CardID");
    }
}

(Nota: invertendo l’ordine tra defaultValue e maxLength sull’ambiente di test viene sollevata un’eccezione Triste, d’altra parte non sarebbe una beta Sorriso). Procediamo con l’aggiornamento (Update-Database). Vediamo cosa è successo:

image

Quello che ci aspettavamo. Utilizzando solo Code First, questo processo di aggiornamento sarebbe risultato distruttivo e comunque avrebbe richiesto un certo effort di modifiche “a manina”. Prima di concludere cerchiamo di aumentare la complessità del modello aggiungendo una tabella Roles ed una relazione uno-a-molti tra le entità Employee e Role:

image

Mettiamo in pending il nuovo schema dati (Add-Migration OfficeDBThirdMigration):

public partial class OfficeDBThirdMigration : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "Roles",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Description = c.String(),
                })
            .PrimaryKey(t => t.Id);
        AddColumn("Employees", "Role_Id", c => c.Int());
        AddForeignKey("Employees", "Role_Id", "Roles", "Id");
        CreateIndex("Employees", "Role_Id");
        DropColumn("Employees", "Role");
        Sql("INSERT INTO Roles (Description) VALUES ('Administrator')");
        Sql("INSERT INTO Roles (Description) VALUES ('Store Manager')");
        Sql("INSERT INTO Roles (Description) VALUES ('Art Director')");
        Sql("UPDATE Employees SET Role_ID = 1 WHERE Name='Mario' AND Surname ='Rossi'");
        Sql("UPDATE Employees SET Role_ID = 2 WHERE Name='Giulio' AND Surname ='Verdi'");
        Sql("UPDATE Employees SET Role_ID = 3 WHERE Name='Pietro' AND Surname ='Libro'");
    }
    public override void Down()
    {
        AddColumn("Employees", "Role", c => c.String());
        DropIndex("Employees", new[] { "Role_Id" });
        DropForeignKey("Employees", "Role_Id", "Roles", "Id");
        DropColumn("Employees", "Role_Id");
        DropTable("Roles");
    }
}

A cui abbiamo aggiunto del codice SQL custom. Un ultimo Update-Database ed il gioco è fatto:

image

Il risultato ovviamente non cambia Sorriso

image

Per eseguire l’upgrade-downgrade del modello dati è sufficiente invocare il metodo Update-Database –targetmigration [parametro] dove [parametro] è uno dei nomi utilizzati in precedenza (ad esempio OfficeDBFirstMigration ). Vedremo nel prossimo post il funzionamento della modalità “automatica” di migrazione.

Print | posted on venerdì 2 dicembre 2011 00:59 | Filed Under [ C# .Net Framework 4.0 Code First Migrations ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET