26

Ho un app MVC4 che ho recentemente aggiornato a Entity Framework 5 e sto cercando di spostare la nostra base di dati oltre a utilizzare le migrazioni dallo stile di sviluppo di cadere e creare ogni corsa.Entity Framework 5 Migrazioni: Impostazione di una migrazione iniziale e seme singolo del database

Ecco quello che ho fatto nella mia funzione di avvio applicazione.

protected void Application_Start() 
{ 
    Database.SetInitializer(
     new MigrateDatabaseToLatestVersion< MyContext, Configuration >()); 
    ... 
} 

Ho eseguito il comando di Enable-Migrations sul mio progetto repository e ho pensato che questo sarebbe creare un file di migrazione iniziale però l'unico file che ha creato era Configuration

Quando si elimina il database si crea come previsto tramite codice e semina il database dal file di configurazione. Nel file di configurazione ho cambiato tutte le funzioni per Add()AddOrUpdate()

Tuttavia esegue la funzione di semi nel mio file Configuration ogni volta che il sito si avvia e duplicati tutti i dati di sementi ancora e ancora.

immaginavo che avrebbe creato un file initial migration come il blog ho letto suggerito che sarebbe e ho potuto inserire i dati di semi in là ma non ha

Qualcuno può spiegare come avrei dovuto essere la creazione di DB nel codice in modo che semini solo una volta?


LINK: The migrations blog post I followed


Mentre questo è molto interessante per l'utilizzo del Migrate.exe EF allora ho passato a utilizzare roundhouse per l'esecuzione di migrazioni. Uso ancora EF per impalcare le mie migrazioni in base ai modelli, ma ho scritto una piccola app per console per scrivere le migrazioni in file SQL. Quindi uso roundhouse per eseguire le migrazioni attraverso i miei script di build rake. C'è un po 'più di processo coinvolto, ma è molto più stabile rispetto all'utilizzo di EF per eseguire le migrazioni al volo all'avvio dell'applicazione.

risposta

38

Questo ha dimostrato di essere un post popolare, quindi l'ho aggiornato alla luce del feedback degli altri. La cosa principale da sapere è che il metodo Seed nella classe Configuration viene eseguito ogni volta che si avvia l'applicazione, che non è ciò che implica il commento nel metodo template. Vedi la risposta di qualcuno a Microsoft a questo post sul perché è così - grazie a Jason Learmouth per averlo trovato.

Se, come me, si desidera eseguire solo gli aggiornamenti del database in caso di migrazioni in sospeso, è necessario eseguire un po 'più di lavoro. È possibile scoprirlo se sono presenti migrazioni in sospeso chiamando migrator.GetPendingMigrations(), ma è necessario farlo nel ctor poiché l'elenco delle migrazioni in sospeso viene cancellato prima che venga chiamato il metodo Seed. Il codice per implementare questa, che va nella classe Migrations.Configuration è la seguente:

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext> 
{ 
    private readonly bool _pendingMigrations; 

    public Configuration() 
    { 
     // If you want automatic migrations the uncomment the line below. 
     //AutomaticMigrationsEnabled = true; 
     var migrator = new DbMigrator(this); 
     _pendingMigrations = migrator.GetPendingMigrations().Any(); 
    } 

    protected override void Seed(MyDbContext context) 
    { 
     //Microsoft comment says "This method will be called after migrating to the latest version." 
     //However my testing shows that it is called every time the software starts 

     //Exit if there aren't any pending migrations 
     if (!_pendingMigrations) return; 

     //else run your code to seed the database, e.g. 
     context.Foos.AddOrUpdate(new Foo { bar = true}); 
    } 
} 

Tengo a precisare che alcune persone hanno suggerito di mettere il codice seme nel 'up' codice di migrazione vera e propria. Funziona, ma significa che è necessario ricordare di inserire il codice seme in ogni nuova migrazione ed è piuttosto difficile da ricordare, quindi non lo farei. Tuttavia, se il tuo seme cambia con ciascuna migrazione, allora quella potrebbe essere una buona strada da percorrere.

+0

Suppongo che venga eseguito ogni volta perché questo è l'unico modo per farlo senza dover implementare il seeding nella migrazione effettiva. Se il tuo codice seme era all'interno di Up() di una migrazione, verrebbe eseguito una volta sola, e per me è più intuitivo, poiché di solito esegui alcune modifiche db e vuoi seminare alcuni dati in questa nuova parte dello schema – FRoZeN

+0

Se non hai alcuna migrazione, il tuo metodo seme non verrebbe mai eseguito con questo metodo. Anche oggi preferisco fare il seeding in una migrazione - in questo modo so che verrà eseguito solo una volta. – oldwizard

+0

Ciao pcguru. Infatti, si ha sempre una migrazione poiché il database iniziale è considerato come una migrazione, ovvero migra da nessun database a un database. Tuttavia posso vedere il vantaggio di avere il seme nella migrazione. –

2

Questo è qualcosa che ho domandato circa in passato troppo. Ho alcune tabelle nel mio database che vengono popolavano nel mio caso dei semi, e ora ho solo controllare per vedere se uno di loro è vuoto all'interno del metodo Seed. Se ci sono righe, il metodo Seed non viene eseguito. Non infallibile, ma fa il trucco.

+1

Questo ha funzionato per prevenire la duplicazione dei dati ma sembra una soluzione un po 'hacky aggiungendo una clausola di uscita. Lascerò la domanda aperta ancora per qualche giorno per vedere se c'è un modo più bello di farlo. – Neil

+0

Sono d'accordo che si tratti di una soluzione hacky - non era un'uscita, più una "Se non ci sono righe allora inseriscici". Puoi quindi ripeterlo per tabelle aggiuntive che potresti inserire in una migrazione. – Richard

6

Si potrebbe aggiungere una migrazione manualmente e riempirlo con qualsiasi codice di semina che vuoi? Nel pacchetto di console direttore di corsa:

Add-Migration [Name] 

È possibile quindi modificare il file che viene creato per voi nella vostra cartella di migrazioni.

Nel mio progetto Io in realtà semina come Richard però nel metodo Seme della configurazione di contesto. Non ho davvero preferenze. Ma le migrazioni dovrebbero essere più efficienti in quanto l'applicazione non ha bisogno di verificare se le file esistono nel database all'avvio dell'applicazione. C'è solo la necessità di verificare se la migrazione è stata eseguita, che dovrebbe essere più veloce.

internal sealed class Configuration : DbMigrationsConfiguration<MyContext> 
{ 
    public Configuration() 
    { 
     // If you want automatic migrations as well uncomment below. 
     // You can use both manual and automatic at the same time, but I don't recommend it. 
     //AutomaticMigrationsEnabled = true; 
     //AutomaticMigrationDataLossAllowed = true; 
    } 

    protected override void Seed(MyContext context) 
    { 
     // This method will be called after migrating to the latest version. 

     // You can use the DbSet<T>.AddOrUpdate() helper extension method 
     // to avoid creating duplicate seed data. 

     context.FontFamilies.AddOrUpdate(
      f => f.Id, 
      new FontFamily { Id = 1, PcName = "Arial" }, 
      new FontFamily { Id = 2, PcName = "Times New Roman" }, 
     }); 

Sto usando questo in Global.asax:

+0

Dovrebbe essere contrassegnato come risposta. Il suggerimento è il sovraccarico di "AddOrUpdate" con il controllo della chiave primaria. Non l'ho riconosciuto la prima volta .. – Sven

2

La risposta a this SO question spiega perché il seme viene eseguito ogni volta che viene eseguita l'app.

Io uso il metodo Jon Smiths, ma ho messo l'assegno per la dichiarazione di migrazioni in attesa in un blocco #if come questo:

#if (!DEBUG) 
      if (!_pendingMigrations) return; 
#endif 

In questo modo quando sto debug il metodo Seed corre sempre per ripopolare la mia seed data: utile quando si eliminano durante i test, ecc. ma non riesco a ottenere il successo perfetto quando sono in rilascio.

Problemi correlati