Datenbank automatisch in Entity Framework Core erstellen

98

Meine Anwendung, die auf .NET Core portiert wird, verwendet den neuen EF Core mit SQLite. Ich möchte die Datenbank- und Tabellenstrukturen automatisch erstellen, wenn die App zum ersten Mal ausgeführt wird. Gemäß der EF-Kerndokumentation erfolgt dies mit manuellen Befehlen

dotnet ef migrations add MyFirstMigration

dotnet ef database update

Ich möchte jedoch nicht, dass der Endbenutzer diese Befehle eingibt, und möchte, dass die App die Datenbank für die erste Verwendung erstellt und einrichtet. Für EF 6 gibt es Funktionen wie

Database.SetInitializer(new CreateDatabaseIfNotExists<MyContext>());

Aber in EF Core scheinen diese nicht zu existieren. Ich kann keine Beispiele oder Dokumentation zu etwas finden, das für den EF-Kern gleichwertig ist, und es wird in der Liste der fehlenden Funktionen in der EF-Kerndokumentation nicht erwähnt. Ich habe die Modellklassen bereits eingerichtet, damit ich Code schreiben kann, um die Datenbank basierend auf den Modellen zu initialisieren, aber es wäre viel einfacher, wenn das Framework dies automatisch tun würde. Ich möchte das Modell nicht automatisch erstellen oder migrieren, sondern nur die Tabellenstrukturen in einer neuen Datenbank erstellen.

Fehlt mir hier etwas oder fehlt die Funktion zum automatischen Erstellen von Tabellen im EF-Kern?

Deandob
quelle

Antworten:

146

Wenn Sie die Migrationen erstellt haben, können Sie sie in der Datei Startup.cs wie folgt ausführen .

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.Migrate();
      }

      ...

Dadurch werden die Datenbank und die Tabellen mithilfe Ihrer hinzugefügten Migrationen erstellt.

Wenn Sie Entity Framework-Migrationen nicht verwenden und stattdessen Ihr DbContext-Modell beim ersten Ausführen genau so erstellen müssen, wie es in Ihrer Kontextklasse ist, können Sie Folgendes verwenden:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.EnsureCreated();
      }

      ...

Stattdessen.

Wenn Sie Ihre Datenbank löschen müssen, bevor Sie sicherstellen können, dass sie erstellt wurde, rufen Sie an:

            context.Database.EnsureDeleted();

Kurz bevor Sie anrufen EnsureCreated()

Angepasst von: http://docs.identityserver.io/en/latest/quickstarts/7_entity_framework.html?highlight=entity

Ricardo Fontana
quelle
1
Ich bin noch ziemlich neu in EF. Ich habe die Klassen erstellt, die die Datenstrukturen mit EF 6-Code definieren. Zuerst "aus Datenbank erstellen" aus Visual Studio unter Verwendung einer aktuellen Datenbankdatei. Ich habe diese dann ausgeschnitten / in die neue Dotnet Core VS-Lösung eingefügt. Ich denke, dies sind keine Migrationen. Bedeutet das, dass ich eine Migrationsdatei erstellen muss, bevor der obige Code verwendet werden kann?
Deandob
2
Es tut mir leid, ich habe einen Fehler in meiner Antwort gemacht. Wenn Sie keine Migrationen verwenden, können Sie den Befehl context.Database.EnsureCreated () / EnsureDeleted () verwenden. Weitere Informationen finden Sie in blogs.msdn.microsoft.com/dotnet/2016 / 09/29 /…
Ricardo Fontana
2
Was ist, wenn Sie beide Lösungen benötigen? Wir haben gerade unsere App auf UWP portiert und EF Core verwendet. Dies bedeutet, dass einige Benutzer die Datenbank von Grund auf neu erstellen müssen, während andere bereits über die Datenbank verfügen. Gibt es eine Möglichkeit, um sicherzustellen, dass bei der ersten Migration die ersten Tabellen nur erstellt werden, wenn sie noch nicht vorhanden sind? Ihre Antwort scheint dies nicht abzudecken, oder fehlt mir etwas?
Lars Udengaard
33

Meine Antwort ist Ricardos Antwort sehr ähnlich, aber ich bin der Meinung, dass mein Ansatz etwas einfacher ist, einfach weil in seiner usingFunktion so viel los ist, dass ich nicht einmal sicher bin, wie genau es auf einer niedrigeren Ebene funktioniert.

Für diejenigen, die eine einfache und saubere Lösung suchen, die eine Datenbank für Sie erstellt, in der Sie genau wissen, was unter der Haube passiert, ist dies Folgendes:

public Startup(IHostingEnvironment env)
{
    using (var client = new TargetsContext())
    {
        client.Database.EnsureCreated();
    }
}

Dies bedeutet so ziemlich, dass Sie innerhalb der von DbContextIhnen erstellten (in diesem Fall meiner TargetsContext) Instanz eine Instanz von verwenden können, DbContextum sicherzustellen, dass die in der Klasse definierten Tabellen erstellt werden, wenn Startup.cs in Ihrer Anwendung ausgeführt wird.

susieloo_
quelle
10
Nur als Information von hier : EnsureCreatedUmgeht Migrationen vollständig und erstellt nur das Schema für Sie. Sie können dies nicht mit Migrationen mischen. EnsureCreatedwurde für Tests oder Rapid Prototyping entwickelt, bei denen Sie die Datenbank jedes Mal löschen und neu erstellen können. Wenn Sie Migrationen verwenden und diese beim Start der App automatisch anwenden möchten, können Sie sie context.Database.Migrate()stattdessen verwenden.
Thomas Schneiter
Dies beantwortet meine Verwirrung, warum meine Verbindungszeichenfolge nicht funktioniert ... :( Die Datenbank wurde also nicht automatisch erstellt, sodass Sie Database.EnsureCreated () angeben müssen, was ich in meinem DbContext-Konstruktor getan habe. Vielen Dank, erspart mir das 3-Tage-Dilemma. X_X
Pampi
Ich wollte nur beachten, dass ich gerade versucht habe, dies in meine Startdatei einzufügen, und VS2019 hat mich informiert, dass dies IHostingEnvironmentjetzt veraltet ist und die empfohlene Alternative ist Microsoft.AspNetCore.Hosting.IWebHostEnvironment.
Strg-Z pls
18

Wenn Sie den Kontext über die Parameterliste von Configure in Startup.cs erhalten, können Sie stattdessen Folgendes tun:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  LoggerFactory loggerFactory,
    ApplicationDbContext context)
 {
      context.Database.Migrate();
      ...
John Pankowicz
quelle
1
IMHO ist dies die beste Lösung: Sie müssen sich nicht um einen Dienst oder gar den DB-Kontext kümmern, sondern können nur den Kontext verwenden, den Sie über die Abhängigkeitsinjektion erhalten. Nett!
Tobias
12

Für EF Core 2.0+ musste ich einen anderen Ansatz wählen, da die API geändert wurde. Ab März 2019 empfiehlt Microsoft, dass Sie Ihren Datenbankmigrationscode in Ihre Anwendungseintragsklasse einfügen, jedoch außerhalb des WebHost-Buildcodes.

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        using (var serviceScope = host.Services.CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetRequiredService<PersonContext>();
            context.Database.Migrate();
        }
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}
Paul Stegler
quelle
7

Wenn Sie keine Migrationen erstellt haben, gibt es zwei Möglichkeiten

1.Erstellen Sie die Datenbank und die Tabellen aus der Anwendung Main:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();

2.Erstellen Sie die Tabellen, wenn die Datenbank bereits vorhanden ist:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();
RelationalDatabaseCreator databaseCreator =
(RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
databaseCreator.CreateTables();

Danke an Bubis Antwort

Nathan Alard
quelle
2
Was sind Ihre Dienstleistungen?
MichaelMao
Alle Dokumentationen, die ich lese, zeigen große, fette Warnungen vor Migrationen mit der Aufschrift "Nicht verwenden EnsureCreatedoder EnsureDeletedoder Database.Migratewerden fehlschlagen"
mwilson,