2013-04-03 24 views
14

Ho imparato a usare EF per una settimana o giù di lì e sono rimasto bloccato sulla questione della creazione/aggiornamento del mio database. Sono in grado di creare un inizializzatore per creare il database se non è lì:Come creare l'inizializzatore per creare e migrare il database mysql?

static class Program 
{ 
    static void Main() 
    { 
     Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer()); 
.... 

class GumpDatabaseInitializer : CreateDatabaseIfNotExists<GumpDatabase> 
{ 
    public GumpDatabaseInitializer() 
    { 
    } 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
     // Other stuff 
    } 
} 

o posso creare una configurazione di migrare il db

static class Program 
{ 
    static void Main() 
    { 
     Database.SetInitializer<GumpDatabase>(new MigrateDatabaseToLatestVersion<GumpDatabase, Configuration>()); 
.... 

internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 

    } 

Ogni funziona correttamente, ma non ho capito un modo per fare entrambe le cose. Posso passare tra i due inizializzatori cambiando la chiamata SetInitializer ma se voglio creare il database se non c'è e anche migrarlo se è cosa faccio? Devo creare un inizializzatore personalizzato?

Grazie

Modifica sulla base di NSGaga rispondono

class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
{ 
    private readonly DbMigrationsConfiguration _configuration; 
    public CreateOrMigrateDatabaseInitializer() 
    { 
     _configuration = new TConfiguration(); 
    } 
    public CreateOrMigrateDatabaseInitializer(string connection) 
    { 
     Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); 

     _configuration = new TConfiguration 
     { 
      TargetDatabase = new DbConnectionInfo(connection) 
     }; 
    } 
    void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
    { 
     Contract.Requires(context != null, "context"); 

     if (context.Database.Exists()) 
     { 
      if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
      { 
       var migrator = new DbMigrator(_configuration); 
       migrator.Update(); 
      } 
     } 
     else 
     { 
      context.Database.Create(); 
      Seed(context); 
      context.SaveChanges(); 
     } 


    } 
    protected virtual void Seed(TContext context) 
    { 
    } 
} 

e

internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     AutomaticMigrationDataLossAllowed = false; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 
    } 
} 

e

class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer<GumpDatabase,Gump.Migrations.Configuration> 
{ 
    public GumpDatabaseInitializer() 
    { 
    } 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Sequences (Name)"); 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX StationPartNumber ON StationPartNumbers (StationId,PartNumberId)"); 
    } 
} 

ed infine

static void Main() 
{ 
    Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer()); 

risposta

16

penso che tu sia più o meno lì - si può cercare il codice sorgente per MigrateDatabaseToLatestVersion (è open source http://entityframework.codeplex.com/) - è piuttosto semplicistico, ciò che fa più o meno è chiamare la DbMigrator - per quanto come ho potuto vedere

Tutto quello che devi fare è unire i due: utilizzare l'uno o l'altro come base, aggiungere altre funzionalità - ciò dovrebbe funzionare correttamente.

class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
{ 
    private readonly DbMigrationsConfiguration _configuration; 
    public CreateAndMigrateDatabaseInitializer() 
    { 
     _configuration = new TConfiguration(); 
    } 
    public CreateAndMigrateDatabaseInitializer(string connection) 
    { 
     Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); 

     _configuration = new TConfiguration 
     { 
      TargetDatabase = new DbConnectionInfo(connection) 
     }; 
    } 
    void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
    { 
     Contract.Requires(context != null, "context"); 

     var migrator = new DbMigrator(_configuration); 
     migrator.Update(); 

     // move on with the 'CreateDatabaseIfNotExists' for the 'Seed' 
     base.InitializeDatabase(context); 
    } 
    protected override void Seed(TContext context) 
    { 
    } 
} 

chiamare in questo modo ...

Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>()); 

... in realtà, sovrascriverlo (dal momento che è implementazione generica) come se stessi facendo per CreateDatabaseIfNotExists (basta 'param' extra per configurazione) - e solo fornire il "seme".

class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration> 
{ 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
    } 
} 

... e lo chiamano qualcosa come

Database.SetInitializer(new GumpDatabaseInitializer()); 

EDIT: Sulla base delle osservazioni - DbMigrator non dovrebbe correre due volte. Controlla sempre (trascorre un po 'di tempo) e fa un aggiornamento' vuoto 'e procede. Tuttavia nel caso in cui, se si desidera rimuovere tale e 'controllare' prima di entrare - questo dovrebbe funzionare (cambiare il pezzo simile sopra) ...

var migrator = new DbMigrator(_configuration); 
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
    if (migrator.GetPendingMigrations().Any()) 
     migrator.Update(); 

(questo è un/doppio controllo ridondante - uno Se l'if-s dovrebbe essere sufficiente, interrompi il tutto e guarda esattamente cosa sta succedendo, non dovrebbe entrare - una volta migrato Db. Come ho detto, funziona bene quando lo provo.

EDIT:

Sostituire l'interno della InitializeDatabase con ...

var doseed = !context.Database.Exists(); 
// && new DatabaseTableChecker().AnyModelTableExists(context); 
// check to see if to seed - we 'lack' the 'AnyModelTableExists' - could be copied/done otherwise if needed... 

var migrator = new DbMigrator(_configuration); 
// if (doseed || !context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
    if (migrator.GetPendingMigrations().Any()) 
     migrator.Update(); 

// move on with the 'CreateDatabaseIfNotExists' for the 'Seed' 
base.InitializeDatabase(context); 
if (doseed) 
{ 
    Seed(context); 
    context.SaveChanges(); 
} 

Questo funziona in tutto (a metà strada), non-semina - se la migrazione inizia per primo. E le migrazioni devono essere le prime, altrimenti hai problemi.

Hai ancora bisogno di farlo correttamente - questo è l'essenza se non tutto ciò di cui potresti aver bisogno - ma in caso di problemi con MySQL ecc., Probabilmente un po 'più di lavoro alle gambe qui.

Nota: Ancora seeding non chiama se si dispone di un db, ma è vuoto. Il problema è la miscelazione dei due diversi inizializzatori. Quindi dovrai farlo - implementando ciò che Crea ... fa all'interno (quella chiamata non possiamo chiamare) o qualcos'altro.

+0

Sapresti dove trovare la fonte per EF 4.3.1? Il sito Microsoft ha solo 5 o 6. – Matt

+0

Penso che siano andati 'open source' solo per i nuovi aggiornamenti (ad esempio EF 6) - ma la fonte dovrebbe essere la stessa - almeno per quanto riguarda questo. Se hai 'DbMigrator' è più o meno lo – NSGaga

+0

Sembra che ci siano alcuni pezzi mancanti:' using System.Data.Entity.Config; using System.Data.Entity.Internal; utilizzando System.Data.Entity.Resources; utilizzando System.Data.Entity.Utilities; 'non trovato in EF 4.3.1. – Matt

1

realtà dovrebbe essere:

var migrator = new DbMigrator(_configuration); 
if (!context.Database.CompatibleWithModel(false) || migrator.GetPendingMigrations().Any()) 
    migrator.Update(); 

perché se abbiamo una migrazione, che non è legato al nostro modello db, ad esempio l'inserimento di una riga in una qualsiasi delle nostre tavole, non verrà eseguita la migrazione .

1

Per eseguire entrambe le operazioni (seed e migrazione), è sufficiente utilizzare le migrazioni con un inizializzatore MigrateDatabaseToLatestVersion. Quando si abilitano le migrazioni per il proprio contesto, viene creata una classe Configuration derivata da DbMigrationsConfiguration ed è possibile sovrascrivere il metodo Seed per eseguire il seeding del database. Si noti che il database potrebbe già contenere i dati seme quando questo metodo viene eseguito, ma il metodo di estensione AddOrUpdate consente di effettuare facilmente "upserts" nel database.

Questo è diverso rispetto al metodo Seed di alcuni degli altri inizializzatori di database in cui il database viene seminato solo quando viene inizialmente creato. Tuttavia, quando si utilizzano le migrazioni, è possibile modificare i dati seme quando il database cambia e l'uso di MigrateDatabaseToLatestVersion lo rende possibile.

Per combinare semina con le migrazioni si dovrà effettuare le seguenti operazioni in un nuovo progetto:

  1. creare un codice-prima DbContext con entità associate

  2. Nella console di gestione dei pacchetti eseguire il comando Enable-Migrations

  3. Nella cartella Migrations una classe Configuration viene generato con un Seed metodo. È possibile modificare questo metodo per seminare il database:

    protected override void Seed(MyContext context) { 
        // Add two entities with name "Foo" and "Bar". 
        context.MyEntities.AddOrUpdate(
        e => e.Name, 
        new MyEntity { Name = "Foo" }, 
        new MyEntity { Name = "Bar" } 
    ); 
    } 
    
  4. È necessario creare un inizializzatore database che deriva da MigrateDatabaseToLatestVersion:

    class MyContextInitializer 
        : MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration> { } 
    

    Sarà inoltre necessario configurare l'inizializzatore sia chiamando Database.SetInitializer(new MyContextInitializer()) quando l'applicazione inizia o nel file App.config utilizzando l'elemento <databaseInitializer/>.

  5. Nel costruttore per la classe generata Configuration è possibile attivare le migrazioni automatiche:

    public Configuration() { 
        AutomaticMigrationsEnabled = true 
    } 
    

    Tuttavia, in una squadra si potrebbe preferire di non farlo. In tal caso dovrai creare una migrazione iniziale (a meno che non sia stata creata quando hai fatto Enable-Migrations). Nel gestore pacchetti eseguire il comando Add-Migration InitialCreate. Questo crea la prima migrazione richiesta per creare il tuo database.

A questo punto si hanno un DbContext con le migrazioni e un metodo Seed.

Quindi per sommarlo: abilitare le migrazioni, utilizzare l'inizializzatore MigrateDatabaseToLatestVersion e aggiungere i dati di inizializzazione nella classe Configuration generata durante le migrazioni abilitate.

0

Mentre MigrateDatabaseToLatestVersion crea effettivamente la DB, se non esiste e permette anche di seme che, se si dispone già di una soluzione di lavoro basata su CreateDatabaseIfNotExists e/o non vogliono complicare con test per l'esistenza di dati di semi, si può semplicemente utilizzare il sotto ereditando da essa, piuttosto che da CreateDatabaseIfNotExists:

public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
     where TContext : DbContext 
     where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
    { 

     void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
     { 
      if (context.Database.Exists()) 
      { 
       if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
       { 
        var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true); 
        migrationInitializer.InitializeDatabase(context); 
       } 
      } 

      base.InitializeDatabase(context); 
     } 
    } 

questo si basa sulle risposte precedenti e propria soluzione di OP. Questo dovrebbe funzionare anche con altri provider, ma ho provato solo con SQL Server.

Problemi correlati