2013-04-03 13 views
14

Ich habe jetzt gelernt, wie EF für eine Woche oder so zu verwenden, und bin auf dem Problem der Erstellung/Aktualisierung meiner Datenbank fest. Ich bin in der Lage einen Initialisierer erstellen, um die Datenbank zu erstellen, wenn sie nicht da ist:Wie Initialisierer zum Erstellen und Migrieren von MySQL-Datenbank erstellen?

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 
    } 
} 

Oder ich kann eine Konfiguration erstellen, um die 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) 
    { 

    } 

Jeder arbeitet korrekt zu migrieren, aber ich habe nicht gedacht, einen Weg, beides zu tun. Ich kann zwischen den beiden Initialisierern wechseln, indem ich den SetInitializer-Aufruf ändere, aber wenn ich die Datenbank erstellen möchte, wenn sie nicht da ist, und sie auch migrieren, wenn es das ist, was soll ich tun? Muss ich einen benutzerdefinierten Initialisierer erstellen?

Dank

bearbeiten basierend auf NSGaga

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) 
    { 
    } 
} 

und

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) 
    { 
    } 
} 

und

antworten
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)"); 
    } 
} 

und schließlich

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

Antwort

16

Ich glaube, Sie so ziemlich da sind - Sie den Quellcode für MigrateDatabaseToLatestVersion Nachschlag kann (es ist Open-Source-http://entityframework.codeplex.com/) - es ist ziemlich simpel ist, was es ziemlich viel kostet die DbMigrator wird nennen - so weit wie ich sehen konnte.

Alles, was Sie tun müssen, scheint die beiden zusammenzuführen - verwenden Sie das eine oder das andere als Basis, fügen Sie andere Funktionalität dort hinzu - das sollte gut funktionieren, denke ich.

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) 
    { 
    } 
} 

nennen es so ...

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

... tatsächlich, außer Kraft setzen sie (da es generische Implementierung ist), wie Sie für CreateDatabaseIfNotExists tun wurden (die Sie gerade haben extra 'param' für Konfiguration) - und liefern Sie einfach den 'Samen'.

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

... und es wie etwas nennen

Database.SetInitializer(new GumpDatabaseInitializer()); 

EDIT: Basierend auf den Kommentaren - DbMigrator sollte nicht zweimal ausgeführt. Es überprüft immer (verbringt ein bisschen Zeit) und führt ein "leeres" Update durch und geht weiter. Allerdings nur für den Fall, wenn Sie möchten, dass entfernen und ‚überprüfen‘ vor dem Eintritt - das sollte funktionieren (das ähnliche Stück oben aus) ...

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

(dies ist eine redundanter/double-check - ein Das if-s sollte ausreichen, legen Sie eine Pause ein - und sehen Sie genau, was passiert, es sollte nicht eindringen - sobald Db migriert ist.Wie ich bereits erwähnt habe, funktioniert es gut, wenn ich es teste.

EDIT:

Ersetzen Sie das Innere des InitializeDatabase mit ...

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(); 
} 

Das funktioniert um (auf halbem Weg) nicht-Aussaat - wenn Migration zuerst geht. Und Migrationen müssen an erster Stelle stehen, sonst hast du Probleme.

Sie müssen es immer noch richtig machen - das ist das Wesentliche, wenn nicht alles, was Sie brauchen könnten - aber wenn irgendwelche Probleme mit MySQL usw., wahrscheinlich ein paar mehr Bein Arbeit hier.

Hinweis: Noch Seeding ruft nicht auf, wenn Sie eine db haben, aber es ist leer. Problem ist das Mischen der zwei verschiedenen Initialisierer. Also müssen Sie das ausarbeiten - entweder indem Sie implementieren, was Create ... in sich tut (diesen Aufruf können wir nicht aufrufen) oder etwas anderes.

+0

Würdest du zufällig wissen, wo du die Quelle für EF 4.3.1 finden kannst? Die Microsoft-Site hat nur 5 oder 6. – Matt

+0

Ich denke, sie sind "Open Source" nur für neue Updates (d. H. EF 6) gegangen - aber die Quelle sollte das gleiche sein - in Bezug auf diese zumindest. Wenn Sie 'DbMigrator' haben, ist es so ziemlich – NSGaga

+0

Es sieht so aus, als gäbe es einige fehlende Teile:' using System.Data.Entity.Config; mit System.Data.Entity.Internal; mit System.Data.Entity.Resources; mit System.Data.Entity.Utilities; 'in EF 4.3.1 nicht gefunden. – Matt

1

Eigentlich sollte es sein:

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

denn wenn wir eine Migration haben, das nicht auf unsere db-Modell verwandt ist, zum Beispiel eine Zeile in einem unserer Tabellen, die Migration wird nicht ausgeführt Einfügen .

1

Um beides zu tun (Seed und migrieren), müssen Sie wirklich nur Migrationen mit einem Initialisierer verwenden. Wenn Sie Migrationen für Ihren Kontext aktivieren, wird eine Configuration-Klasse erstellt, die von DbMigrationsConfiguration abgeleitet wurde. Sie können die Seed-Methode überschreiben, um Ihre Datenbank zu initialisieren. Beachten Sie, dass die Datenbank möglicherweise bereits Seed-Daten enthält, wenn diese Methode ausgeführt wird. Die Erweiterungsmethode AddOrUpdate hilft Ihnen jedoch bei der Erstellung von "Upserts" in Ihrer Datenbank.

Dies ist anders als bei der -Methode einiger anderer Datenbank-Initialisierer, bei denen die Datenbank nur bei der Erstgenerierung gesetzt wird. Wenn Sie jedoch Migrationen verwenden, möchten Sie möglicherweise Ihre Seed-Daten ändern, wenn sich die Datenbank ändert, und dies mithilfe von zu ermöglichen.

So kombinieren Impfen mit Migrationen Sie die folgenden Schritte in einem neuen Projekt zu erfüllen haben:

  1. Erstellen Sie einen Code-first DbContext mit assoziierten Unternehmen

  2. Im Paket-Manager-Konsole ausführen die Befehl Enable-Migrations

  3. im Migrations Ordner eine Configuration Klasse ist mit einemerzeugt 0 Methode. Sie können diese Methode ändern Sie Ihre Datenbank auf Saatgut:

    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. Sie benötigen eine Datenbank initializer erstellen, die von MigrateDatabaseToLatestVersion ableitet:

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

    Sie werden auch die initializer konfigurieren müssen entweder durch Database.SetInitializer(new MyContextInitializer()) Aufruf, wenn Ihre Anwendung startet oder in der App.config Datei mit dem <databaseInitializer/> Element.

  5. im Konstruktor für die Configuration Klasse generiert Sie automatische Migrationen ermöglichen können:

    public Configuration() { 
        AutomaticMigrationsEnabled = true 
    } 
    

    jedoch in einem Team können Sie es vorziehen, dass nicht zu tun. In diesem Fall müssen Sie eine anfängliche Migration erstellen (es sei denn, sie wurde erstellt, als Sie Enable-Migrations erstellt haben). Führen Sie im Paketmanager den Befehl Add-Migration InitialCreate aus. Dies erstellt die erste Migration, die zum Erstellen Ihrer Datenbank erforderlich ist.

An diesem Punkt haben Sie eine DbContext mit Migrationen und eine Seed Methode.

So, um es zusammenzufassen: Aktivieren Sie Migrationen, verwenden Sie den Initialisierer MigrateDatabaseToLatestVersion, und fügen Sie Startdaten in der Klasse Configuration hinzu, die generiert wurde, als Migrationen aktiviert wurden.

0

Während MigrateDatabaseToLatestVersion schaffen tatsächlich die DB, wenn es nicht vorhanden ist und erlauben Ihnen sogar, es zu impfen, wenn Sie bereits eine funktionierende Lösung basierend auf CreateDatabaseIfNotExists haben und/oder will es nicht mit der Prüfung erschweren die Existenz von seed-Daten, können Sie unter der von ihm benutzen Sie einfach eher durch erben als von 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); 
     } 
    } 

Dies basiert auf früheren Antworten und eigene Lösung des OP. Dies sollte auch mit anderen Anbietern funktionieren, aber ich habe nur mit SQL Server getestet.