web-dev-qa-db-fra.com

"Impossible de supprimer la base de données car elle est actuellement utilisée". Comment réparer?

Ayant ce code simple, j'obtiens "Impossible de supprimer la base de données" test_db "car elle est actuellement utilisée" (méthode CleanUp) telle que je l'ai exécutée. 

[TestFixture]
public class ClientRepositoryTest
{
    private const string CONNECTION_STRING = "Data Source=.;Initial Catalog=test_db;Trusted_Connection=True";
    private DataContext _dataCntx;

    [SetUp]
    public void Init()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
        _dataCntx = new DataContext(CONNECTION_STRING);
        _dataCntx.Database.Initialize(true);
    }

    [TearDown]
    public void CleanUp()
    {
        _dataCntx.Dispose();
        Database.Delete(CONNECTION_STRING);
    }
}

DataContext a une propriété comme celle-ci 

 public DbSet<Client> Clients { get; set; }

Comment peut forcer mon code à supprimer la base de données? Merci

54
YMC

Le problème est que votre application conserve probablement encore une connexion à la base de données (ou une autre application conserve également une connexion). La base de données ne peut pas être supprimée s'il existe une autre connexion ouverte. Le premier problème peut probablement être résolu en désactivant le regroupement de connexions (ajoutez Pooling=false à votre chaîne de connexion) ou en effaçant le pool avant de supprimer la base de données (en appelant SqlConnection.ClearAllPools()).

Les deux problèmes peuvent être résolus en imposant la suppression de la base de données, mais pour cela, vous avez besoin d'un initialiseur de base de données personnalisé, dans lequel vous basculez la base de données en mode utilisateur unique puis supprimez-la. Voici voici quelques exemples pour y parvenir.

61
Ladislav Mrnka

Je devenais fou avec ça! J'ai une connexion de base de données ouverte dans SQL Server Management Studio (SSMS) et une requête de table ouverte pour voir le résultat de certains tests unitaires. Lorsque je ré-exécute les tests dans Visual Studio, je souhaite que la drop de la base de données soit toujours EVEN IF la connexion est ouverte dans SSMS.

Voici le moyen idéal pour se débarrasser de Cannot drop database because it is currently in use:

Initialisation de la base de données Entity Framework

L'astuce consiste à surcharger la méthode InitializeDatabase à l'intérieur de la Initializer personnalisée.

Copier la partie pertinente ici dans un souci de goodDUPLICATION ... :)

Si la base de données existe déjà, vous pouvez tomber sur le cas d’avoir une erreur. L'exception «Impossible de supprimer la base de données car il s'agit actuellement de en cours d'utilisation »peut augmenter. Ce problème se produit lorsqu'une connexion active reste connecté à la base de données en cours de traitement supprimé. Une astuce consiste à remplacer la méthode InitializeDatabase et à modifier la base de données. Cela indique à la base de données de fermer toutes les connexions et si une transaction est ouverte pour annuler celle-ci.

public class CustomInitializer<T> : DropCreateDatabaseAlways<YourContext>
{
    public override void InitializeDatabase(YourContext context)
    {
        context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction
            , string.Format("ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE", context.Database.Connection.Database));

        base.InitializeDatabase(context);
    }

    protected override void Seed(YourContext context)
    {
        // Seed code goes here...

        base.Seed(context);
    }
}
36
Leniel Maccaferri

Il s’agit d’un (ré) initialiseur de base de données très agressif pour le code EF en premier avec les migrations; utilisez-le à vos risques et périls, mais il semble fonctionner assez souvent pour moi. Ce sera;

  1. Déconnecter de force tous les autres clients de la base de données
  2. Supprimer la base de données.
  3. Reconstruisez la base de données avec les migrations et exécutez la méthode Seed
  4. Prenez des âges! (surveillez la limite de délai d'attente pour votre infrastructure de test; un délai d'attente de 60 secondes par défaut pourrait ne pas être suffisant)

Voici la classe.

public class DropCreateAndMigrateDatabaseInitializer<TContext, TMigrationsConfiguration>: IDatabaseInitializer<TContext> 
    where TContext: DbContext
    where TMigrationsConfiguration : System.Data.Entity.Migrations.DbMigrationsConfiguration<TContext>, new()
{
    public void InitializeDatabase(TContext context)
    {
        if (context.Database.Exists())
        {
            // set the database to SINGLE_USER so it can be dropped
            context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");

            // drop the database
            context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
        }

        var migrator = new MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>();
        migrator.InitializeDatabase(context);

    }
}

Utilisez-le comme ça;

public static void ResetDb()
{
    // rebuild the database
    Console.WriteLine("Rebuilding the test database");
    var initializer = new DropCreateAndMigrateDatabaseInitializer<MyContext, MyEfProject.Migrations.Configuration>();
    Database.SetInitializer<MyContext>initializer);

    using (var ctx = new MyContext())
    {
        ctx.Database.Initialize(force: true);
    }
}

J'utilise également le truc 'Pooling = false' de Ladislav Mrnka, mais je ne suis pas sûr que ce soit nécessaire ou juste une mesure de ceinture et d'accolades. Cela contribuera certainement à ralentir davantage le test. 

17
Steve Cooper

Aucune de ces solutions n'a fonctionné pour moi. J'ai fini par écrire une méthode d'extension qui fonctionne:

private static void KillConnectionsToTheDatabase(this Database database)
{
    var databaseName = database.Connection.Database;
    const string sqlFormat = @"
             USE master; 

             DECLARE @databaseName VARCHAR(50);
             SET @databaseName = '{0}';

             declare @kill varchar(8000) = '';
             select @kill=@kill+'kill '+convert(varchar(5),spid)+';'
             from master..sysprocesses 
             where dbid=db_id(@databaseName);

             exec (@kill);";

    var sql = string.Format(sqlFormat, databaseName);
    using (var command = database.Connection.CreateCommand())
    {
        command.CommandText = sql;
        command.CommandType = CommandType.Text;

        command.Connection.Open();

        command.ExecuteNonQuery();

        command.Connection.Close();
    }
}
5
Chris McKenzie

J'essaie d'ajouter Pooling=false comme l'a dit Ladislav Mrnka mais j'ai toujours l'erreur.
J'utilise SQL Server Management Studio et même si je ferme toute la connexion, j'obtiens l'erreur.

Si je ferme SQL Server Management Studio alors la base de données est supprimée :)
J'espère que cela peut aider

1
anthoLB29

J'ai eu la même erreur. Dans mon cas, je viens de fermer la connexion à la base de données puis de me reconnecter une fois le nouveau modèle ajouté et un nouveau contrôleur échafaudé. C'est cependant une solution très simple et déconseillée pour tous les scénarios si vous souhaitez conserver vos données.

0
user5504242

J'ai eu le même problème à l'époque. Il s'avère que la solution consiste à fermer la connexion dans l'onglet Explorateur de serveurs de Visual Studio. Alors, vous pourriez peut-être vérifier si la connexion est toujours ouverte dans l'Explorateur de serveurs.

0
Sambalado