web-dev-qa-db-fra.com

Erreur d'utilisation de la base de données avec Entity Framework 4 Code First

J'ai une application MVC3 et EF 4 Code First, configurée pour modifier la base de données lorsque le modèle change, en définissant l'initialiseur de base de données sur DropCreateDatabaseIfModelChanges<TocratesDb>, où TocratesDb est ma dérivée DbContext.

J'ai maintenant apporté une modification au modèle en ajoutant des propriétés à une classe, mais lorsque EF tente de supprimer et de recréer la base de données, le message d'erreur suivant s'affiche:

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

Je n'ai absolument aucune autre connexion ouverte sur cette base de données. Je suppose que mon cDbContext a toujours une connexion ouverte à la base de données, mais que puis-je faire à ce sujet?

NOUVEAU: Maintenant, mon problème est de savoir comment recréer la base de données en fonction du modèle. En utilisant le plus général IDatabaseInitializer, je le perds et je dois le mettre en œuvre moi-même. 

51
ProfK

Votre contexte actuel doit avoir une connexion ouverte pour pouvoir supprimer la base de données. Le problème est qu’il peut y avoir d’autres connexions ouvertes qui bloqueraient votre initialiseur de base de données. Un très bel exemple est d’avoir ouvert n’importe quelle table de votre base de données dans Management Studio. Un autre problème possible peut être les connexions ouvertes dans le pool de connexions de votre application.

Cela peut être évité en MS SQL, par exemple, en passant de la base de données à SINGLE USER et en forçant la fermeture de toutes les connexions et l'annulation des transactions incomplètes:

ALTER DATABASE Tocrates SET SINGLE_USER WITH ROLLBACK IMMEDIATE

Vous pouvez créer un nouvel intializer qui appellera d'abord cette commande puis supprimera la base de données. Sachez que vous devez gérer vous-même une connexion à une base de données, car ALTER DATABASE et DROP DATABASE doivent être appelés sur la même connexion.

Modifier:

Ici, vous avez des exemples utilisant un motif de décorateur. Vous pouvez le modifier et initialiser l'initialiseur interne à l'intérieur du constructeur au lieu de le transmettre en tant que paramètre.

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

J'ai trouvé dans EF 6 que cela échoue avec une erreur ALTER DATABASE statement not allowed within multi-statement transaction.

La solution consistait à utiliser la nouvelle surcharge de comportement de transaction comme ceci:

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

J'ai eu le même problème.

Je l'ai résolu en fermant une connexion ouverte sous la vue Explorateur de serveurs de Visual Studio. 

21
psy

Je me rends compte que c'est daté mais je ne pouvais pas faire fonctionner la solution acceptée alors j'ai lancé une solution rapide ...

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

Cela fonctionne pour moi et prend facilement en charge les semis.

13
Dave Jellison

Dans Visual Studio 2012, la fenêtre SQL Server Object Explorer peut contenir une connexion à la base de données. La fermeture de la fenêtre et de toutes les fenêtres ouvertes libère la connexion.

3
Edward Brey

Une simple fermeture de tout mon projet et sa réouverture ont fait l'affaire pour moi. C'est le moyen le plus simple de s'assurer qu'il n'y a pas de connexions encore ouvertes

0
user6302183