Dieser Artikel wurde maschinell übersetzt.

Datenpunkte

Ein Code ersten Migrationen Rätsel: Gelöst

Julie Lerman

Julie LermanEin Freund fragte mich vor kurzem eine Frage zu Entity Framework (EF) Code ersten Migrationen, die mich verwirrt, fast so viel, wie er ihn verwechselt — aber aus verschiedenen Gründen.

Das Gespräch ging etwa so:

"Hey Julie, warum meine Azure Web Site automatisch meine Datenbank migrieren würde?"

"Sind Sie sicher, Sie haben keine automatische Migrationen auf True festgelegt? Es ist standardmäßig auf false festgelegt."

"Definitiv nicht. Ich diese Einstellung ändern nicht."

"Was die Migrationen-Initialisierung? Das könnte es auch tun."

Lass mich hier unterbrechen und stellen sicher, dass Sie wirklich über die Initialisierung zu verstehen. Wenn Migrationen explizit sind, etwas erstellen und führen Sie in der Paket-Manager -Konsole, warum gibt es eine Initialisierung?

Code ersten Initialisierungen

Code begann mit drei Initialisierungen:

CreateDatabaseIfNotExists: Damit ein, wenn die Datenbank nicht es gefunden erstellt werden, und das ist das Ende für seine Arbeit. Wenn Sie die Datenbank ändern, werden nicht die Datenbank neu erstellt oder migriert. Sie erhalten einfach eine Ausnahme, was darauf hindeutet, dass Sie eine andere Methode verwenden, um die Datenbank zu verwalten. Insbesondere werden die Anregung, Migrationen zu verwenden.

DropCreateDatabaseAlways: Dieser ist ideal für Integrationstests. Die vorhandene Datenbank zu löschen wird und neu erstellen, aus dem nichts, keine Samen-Logik in hinzugefügten ausgeführt. Für Integrationstests, ist es eine gute Möglichkeit, immer eine konsistente Datenbank haben.

DropCreateDatabaseIfModelChanges: Bevor es Migrationen gab, war dies eine hilfreiche Option während der Entwicklung, wenn Sie nicht brauchen, Daten oder anderen Schema, die Sie in der Datenbank, z. B. das Hinzufügen von Indizes oder Trigger geändert haben könnte.

Der Vollständigkeit halber möchte ich auch darauf hinweisen, dass im EF6, wir die NullDatabaseInitializer für diejenigen, die nur erinnern lernten (oder entdecken) die andere Syntax schwer für das Deaktivieren der Initialisierung: SetDatabaseInitialier < MyContext > (Null).

Dann kam Migrationen in EF4.3. Migrationen sind tätig in der Datenschicht nicht, bis Sie explizit sie aus dem Paket-Manager -Konsole-Fenster aktivieren, indem Sie eingeben Aktivieren-Migrationen. Alles, was dies wirklich bedeutet ist, einen neuen Ordner mit einer Configuration-Klasse zum Projekt hinzufügen. Wenn der Prozess aktivieren eine vorhandene Datenbank, die an den Kontext gebunden entdeckt, wird eine erste Migration-Klasse erstellt. Dadurch wird sichergestellt, dass beim Ausführen von Migrationen Code ersten versuchen nicht, ein bereits vorhandene Datenbankobjekt erstellen.

In der Tat, Sie sehen eine Informationsmeldung in der Konsole nach dem Aktivieren von Migrationen, siehe Abbildung 1.

Die Enable-Migrationen-Reaktion auf vorhandene Datenbank, die erstellt wurde eine Initialisierung
Abbildung 1: Die Enable-Migrationen-Reaktion auf vorhandene Datenbank, die erstellt wurde eine Initialisierung

Wenn Sie in Ihrer Datenbank, in der neuen __MigrationHistory-Datei suchen, sehen Sie eine Zeile in der Tabelle widerspiegelt, dass die erste Migration bereits in der Datenbank ausgeführt wurde.

Es ist wichtig zu verstehen, es gibt nichts anderes — keine Ergänzungen Ihrer Anwendung Config oder irgendwelche anderen versteckten Stücke für die Migration-Rätsel. Die Configuration-Klasse ist kritisch.

Hier ist die Standardimplementierung von wenn ich Migra aktiviert­gen auf meine Datenschicht, wo ich eine DbContext habe Klasse namens MigratoryBirdsContext:

internal sealed class Configuration : 
  DbMigrationsConfiguration<MigratoryBirdsContext>
  {
    public Configuration()
    {
      AutomaticMigrationsEnabled = false;
      ContextKey = "DataLayer.MigratoryBirdsContext";
    }
    protected override void Seed(MigratoryBirdsContext context)
    {
    }
  }

Weil ich diese Klasse explizit verwenden werde weiter, ändere ich die Signatur zu öffentlich. Für diejenigen unter Ihnen einfach den Code Scannen ist hier die neue Deklaration:

public class Configuration : DbMigrationsConfiguration<MigratoryBirdsContext>

Migrationen können automatisch ausführen, was bedeutet, dass Modelländerungen entdecken zu und Migrationen, die entsprechenden Änderungen werden erstellt und auf der Datenbank ausgeführt. Dies alles geschieht zur Laufzeit während der Datenbankinitialisierung. Automatische Migrationen sind praktisch für einfache Anwendungen, aber Sie haben sehr wenig Kontrolle über sie und in der Regel nicht zu empfehlen es ihnen ermöglichen. Ich war glücklich, wenn Code erste standardmäßig auf False geschaltet.

Zur Laufzeit Code erste sucht eine DbMigrationsConfiguration-Klasse, die an einen Kontext gebunden ist, wenn diesem durch seine Daten geht­Basis-Initialisierung. Wenn dieser Klasse gefunden wird, explizite Aufrufe oder Einstellungen für Datenbankinitialisierung eines der ursprünglichen drei Initialisierungen fest — deren Aufgabe es ist zu erstellen oder löschen und erstellen die Datenbank — erstellt die Datenbank mit der Völkerwanderung. Dies ist ein neues Muster für EF6.1. Davor würden diese drei Initialisierungen der Initialisierungslogik verwenden sie immer EF4.3, verwendet, da hatte durch das Datenbankschema aus dem Modell und Konfigurationen herleiten. Diese Änderung EF6.1 eignet sich für Entwickler, die, wie ich, die DropCreateDatabase verwenden­immer Initialisierung in ihre Tests. Es wird nun die Datenbank löschen sondern meinen Migrationen verwenden, um es wiederherzustellen. Wenn ich meine Migrationen-Klassen angepasst habe, werde ich diese Anpassung in meine Datenbank, sowie übernommen bekommen.

Es ist auch wichtig zu beachten, dass die Standard-Initialisierung — CreateDatabaseIfNotExists — wird auch Migrationen verwenden, um eine Datenbank zu erstellen, wenn die Migrationen vorhanden, aber die Datenbank nicht.

Also, kann jetzt ich schaue auf die vierte Initialisierung, MigrateDatabaseToLatestVersion, ist derjenige, den ich meinen Freund gefragt, als er versuchte, die Quelle der geheimnisvollen Migrationen in seine Azure Web-Site zu erreichen. Diese Initialisierung wurde um Migrationen in EF4.3 eingeführt wurden.

Der Name, MigrateDatabaseToLatestVersion, scheint darauf hinzudeuten, es würde funktionieren ähnlich wie automatische Migrationen, aber es gibt ein einziger, signifikanter Unterschied. Automatische Migrationen werden durch eine Änderung im Modell ausgelöst, während dieser Initialisierer Aufgabe von bestehenden Migration Klassen ausgelöst wird.

Um zu sehen, wie das funktioniert, beginnen wir mit den Standardzustand nachdem Sie Migrationen aktiviert haben. Im Spiel gibt es keine MigrateDatabaseToLatestVersion-Initialisierung und AutomaticMigrations auf False festgelegt ist.

Hier ist ein grundlegende Integrationstest, der eine einfache Abfrage auf MigratoryBirdsContext ausgeführt wird:

[TestMethod]
public void CanInitializeDatabase() {
  using (var context = new MigratoryBirdsContext())
  {
    context.MigratoryBirds.ToList();
  }
  Assert.Inconclusive("If we're here, it did not crash");
}

Würde ich eine Änderung an einer der Klassen in meinem Modell vornehmen, und führen Sie diesen Test, würde eine Ausnahme ausgelöst werden, mir zu sagen, "das Modell sichern MigratoryBirdsContext Rahmen geändert hat da die Datenbank erstellt wurde., Verwenden Sie Code ersten Migrationen zum Aktualisieren der Datenbank."

Obwohl Migrationen aktiviert wurden und ich die MigrationsConfiguration-Klasse habe, dass EF weiß sind, die entspricht das aktuelle Modell nicht die neueste Version, die in der MigrationHistory-Tabelle gespeichert. An dieser Stelle ich könnte hinzufügen-Migration und Update-Datenbank verwenden, um das Problem zu beheben, aber ich möchte zeigen, wie die Initialisierung funktioniert, so dass ich nehme einen anderen Weg: Ich werde den Test um die Initialisierung zu aktivieren ändern. Wenn Sie nicht mit ihrer Konstruktorsyntax vertraut sind, musst du auf die Konfigurationsdatei zusammen mit dem Ziel DbContext, zeigen wie folgt:

[TestMethod]
public void CanInitializeDatabase()
{
  Database.SetInitializer(
    new MigrateDatabaseToLatestVersion<MigratoryBirdsContext, 
    DataLayer.Migrations.Configuration>());
  using (var context = new MigratoryBirdsContext())
  {
    context.MigratoryBirds.ToList();
  }
  Assert.Inconclusive("If we're here, it did not crash");
}

Eine weitere Ausnahme löst den Test erneut ausführen, und seine Botschaft hebt die Unterschiede für diese Initialisierung und automatische Migrationen: "Konnte nicht aktualisiert die Datenbank entsprechend das aktuelle Modell, denn es gibt ausstehende Änderungen und automatische Migration deaktiviert. Entweder schreiben Sie die ausstehenden Modelländerungen in einer Code-basierte Migration oder die automatische Migration."

AutomaticMigrations beachten Sie den Unterschied, die Migration zu erstellen und aktualisieren Sie die Datenbank würde — alles on-the-Fly und leise. Jedoch beibehalten nicht AutomaticMigrations Migrationen, die sie als Klasse in Ihrem Projekt erstellt. Obwohl diese Initialisierung die Änderung stellt fest, nicht automatisch die Migration für Sie erstellt werden. Es kann nur mit vorhandenen Migrationen arbeiten.

Wenn Sie manuell zuvor Migrationen durchgeführt haben, ist es ein zweistufiger Prozess:

Zunächst erstellen Sie die Migration mit dem Befehl Add-Migration. Dies wird Ihr Modell und die Abfrage der Datenbank für den neuesten Eintrag in der Migration-History-Tabelle, dann vergleichen Sie die beiden lesen. Wenn Ihr Modell geändert hat, seit sie zuletzt gespeichert wurde, erstellt es eine neue Migration-Klasse mit Anleitungen für die Migration des aktuellen Schemas der Datenbank mit den Änderungen in Ihrem Modell ausrichten. Als nächstes führen Sie die Migration (oder eine Kombination von Migrationen, die auf der Grundlage der Befehlssyntax) auf der Datenbank. Basis des Befehls in der Paket-Manager -Konsole eingegebenen ist Update-Datenbank.

Nicht viele Entwickler klar, dass Sie die Updatedatenbank durchführen können, programmatisch, sowie. Ich wusste, es war möglich, aber noch nicht getan so lange musste ich tatsächlich die Details in meinem Code ersten Migrationen-Kurs zur Pluralsight suchen gehen. Die Migrationen-API verfügt über eine DbMigrator-Klasse, mit dem Sie die Datenbank mit den gleichen Optionen aktualisieren Sie auf von der Update-Database-Befehl in der Konsole zugreifen.

Die wichtigsten Schritte sollen Ihre Migration-Konfigurationsdatei instanziieren; Instanziieren Sie die API-DbMigrator-Klasse, Übergabe einer Instanz der Migration Configuration-Klasse; und dann Update aufrufen:

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

Und das macht die MigrateDatabaseToLatestVersion-Initialisierung in Ihrem Namen. Die Migrationen müssen bereits vorhanden sein, in Ihrem Projekt (oder in Ihrer kompilierten Assembly) und die Initialisierung wird führen sie auf der Datenbank für Sie zur Laufzeit.

Mit dieser Veränderung, die ich meine Domain-Klasse vorgenommen haben, werde ich jetzt weitermachen und fügen Sie eine Migration mit dem Befehl hinzufügen-Migration "JetStream." Hier ist die Klasse, die für mich generiert wird:

public partial class JetStream : DbMigration
{
  public override void Up()
  {
    AddColumn("dbo.MigratoryBirds", "DependentOnJetStream", c =>
      c.Boolean(nullable: false));
  }
  public override void Down()
  {
    DropColumn("dbo.MigratoryBirds", "DependentOnJetStream");
  }
}

Diese Klasse ist jetzt Teil meines Projektes. Wenn ich den Test erneut ausführen, wo ich eingerichtet haben die Migration -­Datenbank Initialisierung, der Test keine Ausnahme. Und mein Profiler zeigt mir, dass vor dem SELECT Wesen ausgeführt, das Schema der Tabelle wird geändert, um in die neue DependentOnJetStream-Eigenschaft hinzufügen.

Was ich nicht mit diesem Muster erhalte, die ich über die DropCreateDatabaseAlways-Initialisierung gerne ist eine neue Version der Datenbank für Integrationstests. Um dies zu erreichen, habe ich einige rohe SQL in der Seed-Methode der Konfigurationsdatei vor der Aussaat der Datenbank ausgeführt werden. Oder für die Integrationstests, ich könnte einfach Initialisierung vollständig deaktivieren und die Datenbankerstellung explizit zu steuern. Eric Hexter schrieb einen große Blog-Beitrag dazu, "Using SQL Compact für Integrationstests mit Entity Framework(bit.ly/1qgh9Ps).

Wo scheint MigrateDatabaseToLatestVersion

Wären Sie zu kompilieren Sie das Projekt und geben es an einen anderen Entwickler, der ihre eigene Entwicklungsdatenbank migriert hat nicht, würde die Initialisierung die app zu suchen, eine Migration auslösen. Die Migration wird gefunden werden, da es im Projekt vorhanden ist. Die Initialisierung der Datenbank Migration History-Tabelle überprüfen und das Datenbankschema zu aktualisieren, falls erforderlich.

Wenn man bedenkt das in Produktion, keine Entwicklung, die Migration-Klasse werden in Ihre Assembly kompiliert und als Teil der Anwendung bereitgestellt. Wenn die Anwendung ausgeführt wird, wird es auf eine MigrateDatabaseToLatestVersion-Einstellung (an der entsprechenden Stelle in der Anwendung) und das Datenbankschema entsprechend zu aktualisieren.

Was passiert in der Azure-Website?

Nun, da Sie ein gutes Verständnis der wie MigrateDatabase­ToLatestVersion funktioniert, gehen wir zurück zum Gespräch mit meinem Freund. Wir aufgehört haben, an: "Was die Migrationen-Initialisierung? Das würde es auch tun."

Er antwortete, dass er die Migrationen-Initialisierung an einer beliebigen Stelle im Code festgelegt hatte nicht. So, jetzt war es ein großes Rätsel. Das Verhalten der MigrateDatabaseToLatestVersion genau abgestimmt. Er entsandte seine app in Azure. Wenn er es lief, die app bemerkt die Veränderung, die er im Modell vor Umgruppierung gemacht hatte und das Datenbankschema aktualisiert.

Mit all diesen Fragen gestellt und beantwortet, ließ mich ein bisschen verwirrt ich ging zu meinem Lieblings-Suchmaschine mit einigen nützlichen Informationen in der hand und machte eine Entdeckung.

Wenn Sie eine Webanwendung auf Azure veröffentlichen, gibt es eine Checkbox auf der Einstellungsseite ist standardmäßig deaktiviert. Aber diese Checkbox sagt "Execute Code erste Migrationen (läuft auf Programmstart)."  Überprüfen dies wird die MigrateDatabaseToLatestVersion-Einstellung in der Konfigurationsdatei deklarativ hinzufügen.

Ich mein Freund gemailt und zeigte ihm einen Screenshot der Seite mit dieser Einstellung und fragte: "bekannt vor?" Rätsel gelöst. Er erstellt die Migrationen und seine Entwicklungsdatenbank aktualisiert. So wurden die Migrationen in seine Assembly kompiliert, wenn er es bereitgestellt. Und er erinnerte sich an diese Checkbox ankreuzen.

Also wenn er seine Web-Site zum ersten Mal lief, die Initialisierung DbMigrator.Update genannt, die die Liste der Migrationen in der Assembly in die Liste der MigrationsHistory-Tabelle verglichen und lief, waren nicht in der Tabelle vorhanden.

Jetzt erkennt er, dass als Feature, kein Rätsel.


Julie Lerman ist als Microsoft MVP, .NET-Mentor und Unternehmensberaterin tätig und wohnt in den Bergen von Vermont. Hier finden Sie ihre Präsentation auf Datenzugriff und andere .NET Themen auf Benutzergruppen und Konferenzen auf der ganzen Welt. She bloggt auf thedatafarm.com/blog und ist der Autor von "Programmierung Entity Framework" (2010), sowie eine Code-First Edition (2011) und eine DbContext Ausgabe (2012), alle von O' Reilly Media. Folgen Sie ihr auf Twitter unter twitter.com/julielerman, und besuchen Sie ihre Pluralsight-Kurse unter juliel.me/PS-Videos.

Unser Dank gilt dem folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Rowan Miller