it-swarm.com.de

Fehler beim Verwenden der Datenbank mit Entity Framework 4 Code First

Ich habe eine MVC3- und EF 4 Code First-Anwendung, die so konfiguriert ist, dass sie den DB ändert, wenn sich das Modell ändert, indem der DB-Initialisierer auf DropCreateDatabaseIfModelChanges<TocratesDb> gesetzt wird, wobei TocratesDb meine abgeleitete DbContext ist.

Ich habe jetzt eine Änderung am Modell vorgenommen, indem ich Eigenschaften zu einer Klasse hinzugefügt habe. Wenn EF jedoch versucht, die Datenbank zu löschen und neu zu erstellen, erhalte ich die folgende Fehlermeldung:

Cannot drop database "Tocrates" because it is currently in use.

Ich habe absolut keine anderen Verbindungen in dieser Datenbank. Ich gehe davon aus, dass mein cDbContext noch eine offene Verbindung zur Datenbank hat. Was kann ich dagegen tun?

NEU: Nun ist mein Problem, wie man die Datenbank basierend auf dem Modell neu erstellt. Mit dem allgemeineren IDatabaseInitializer verliere ich das und muss es selbst implementieren. 

51
ProfK

Ihr aktueller Kontext muss über eine geöffnete Verbindung verfügen, um die Datenbank löschen zu können. Das Problem ist, dass es andere geöffnete Verbindungen geben kann, die Ihren db-Initialisierer blockieren. Ein sehr schönes Beispiel ist das Öffnen einer beliebigen Tabelle aus Ihrer Datenbank im Management Studio. Ein anderes mögliches Problem können geöffnete Verbindungen im Verbindungspool Ihrer Anwendung sein.

In MS SQL kann dies beispielsweise vermieden werden, indem die DB in den SINGLE USER-Modus versetzt wird und alle Verbindungen geschlossen werden und unvollständige Transaktionen rückgängig gemacht werden:

ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE

Sie können einen neuen Intializer erstellen, der diesen Befehl zuerst aufruft und dann die Datenbank löscht. Beachten Sie, dass Sie eine Datenbankverbindung selbst behandeln sollten, da ALTER DATABASE und DROP DATABASE für dieselbe Verbindung aufgerufen werden müssen.

Bearbeiten:

Hier haben Sie ein Beispiel mit dem Dekoratormuster. Sie können ihn modifizieren und den inneren Initialisierer innerhalb des Konstruktors initialisieren, anstatt ihn als Parameter zu übergeben.

public class ForceDeleteInitializer : IDatabaseInitializer<Context>
{
    private readonly IDatabaseInitializer<Context> _initializer;

    public ForceDeleteInitializer(IDatabaseInitializer<Context> innerInitializer)
    {
        _initializer = innerInitializer;    
    }

    public void InitializeDatabase(Context context)
    {
        context.Database.SqlCommand("ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
        _initializer.InitializeDatabase(context);
    }
}
46
Ladislav Mrnka

Ich habe in EF 6 festgestellt, dass dies mit einem ALTER DATABASE statement not allowed within multi-statement transaction-Fehler fehlschlägt.

Die Lösung bestand darin, die neue Überlastung des Transaktionsverhaltens folgendermaßen zu verwenden:

context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
41
Kevin Kuszyk

Ich hatte das gleiche Problem.

Ich löste es, indem ich eine Verbindung unter der Ansicht "Server-Explorer" von Visual Studio schließe. 

21
psy

Ich weiß, das ist veraltet, aber ich konnte die akzeptierte Lösung nicht zum Laufen bringen, also habe ich eine schnelle Lösung gerollt ...

using System;
using System.Data.Entity;

namespace YourCompany.EntityFramework
{
    public class DropDatabaseInitializer<T> : IDatabaseInitializer<T> where T : DbContext, new()
    {
        public DropDatabaseInitializer(Action<T> seed = null)
        {
            Seed = seed ?? delegate {};
        }

        public Action<T> Seed { get; set; }

        public void InitializeDatabase(T context)
        {
            if (context.Database.Exists())
            {
                context.Database.ExecuteSqlCommand("ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
                context.Database.ExecuteSqlCommand("USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
            }

            context.Database.Create();

            Seed(context);
        }
    }
}

Das funktioniert für mich und erleichtert das Aussaat.

13
Dave Jellison

In Visual Studio 2012 kann das Fenster SQL Server Object Explorer eine Verbindung zur Datenbank halten. Das Schließen des Fensters und aller daraus geöffneten Fenster gibt die Verbindung frei.

3
Edward Brey

Ein einfaches Schließen meines gesamten Projekts und das erneute Öffnen haben für mich den Trick gebracht. Dies ist der einfachste Weg, um sicherzustellen, dass noch keine Verbindungen bestehen

0
user6302183