59 votes

"Impossible de supprimer la base de données car elle est en cours d'utilisation". Comment résoudre ce problème ?

Avec ce code simple, j'obtiens "Cannot drop database "test_db" because it is currently in use" (méthode CleanUp) lorsque je l'exécute.

[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 forcer mon code à supprimer la base de données ? Merci

0 votes

Le CTP5 est mort. La version actuelle est EF 4.1.

70voto

Ladislav Mrnka Points 218632

Le problème est que votre application a probablement encore une connexion avec la base de données (ou une autre application a é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 la mise en commun des connexions (ajoutez la commande Pooling=false à votre chaîne de connexion) ou videz le pool avant de supprimer la base de données (en appelant SqlConnection.ClearAllPools() ).

Ces deux problèmes peuvent être résolus en forçant la base de données à être supprimée, mais pour cela, vous devez utiliser un initialisateur de base de données personnalisé dans lequel vous faites passer la base de données en mode mono-utilisateur, puis la supprimez. Ici est un exemple de la manière d'y parvenir.

4 votes

Pooling=false a fait le travail. Merci !

2 votes

@LadislavMrnka Et si j'ai pooling=false et que j'ai déjà défini single_user, mais que je reçois toujours ce message d'erreur ?

43voto

Leniel Macaferi Points 38324

Je devenais fou avec ça ! J'ai une connexion ouverte à la base de données 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 veux qu'il y ait drop la base de données est toujours MÊME SI la connexion est ouverte dans SSMS.

Voici la façon définitive de 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 à remplacer InitializeDatabase dans la méthode personnalisée Initializer .

Copié la partie pertinente ici pour le bien de good DUPLICATION ... :)

Si la base de données existe déjà, vous risquez de tomber sur le cas suivant une erreur. L'exception "Cannot drop database because it is currently en cours d'utilisation" peut se produire. Ce problème se produit lorsqu'une connexion active reste connectée à la base de données qu'elle est en train de supprimée. Une astuce consiste à remplacer l'option Init modifier la base de données. Cela indique à la base de données de fermer toutes les connexions et si une transaction est ouverte, de revenir en arrière.

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

1 votes

C'était le moyen le plus simple et le plus direct de supprimer des utilisateurs pour les tests automatisés.

2 votes

Ajouter IF EXISTS et c'est parfait.

0 votes

@ericosg Comment utiliser IF EXISTS ?

18voto

Steve Cooper Points 6637

Il s'agit d'un (ré)initialisateur de base de données vraiment agressif pour EF code-first avec des migrations ; utilisez-le à vos risques et périls, mais il semble fonctionner de manière assez répétée pour moi. Il le fera ;

  1. Déconnexion forcée de tous les autres clients de la base de données.
  2. Supprimez la BD.
  3. Reconstruction de la base de données avec les migrations et exécution de la méthode Seed.
  4. Prenez votre temps ! (surveillez le délai d'attente de votre cadre de test ; un délai d'attente par défaut de 60 secondes peut 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 l'astuce "Pooling=false" de Ladislav Mrnka, mais je ne sais pas si elle est nécessaire ou s'il s'agit simplement d'une mesure de sécurité. Cela contribuera certainement à ralentir davantage le test.

5voto

Chris McKenzie Points 1023

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

0 votes

Au lieu d'utiliser database.Connection.CreateCommand() on peut utiliser dbContext.Database.ExecuteSqlCommand(TransactionalBehavior.D‌​oNotEnsureTransactio‌​n, sqlString) . Sinon, c'est une bonne solution.

0 votes

Aidez-moi à comprendre, s'il vous plaît - quel est le problème avec database.Connection.CreateCommand() ? Je n'ai pas eu de problèmes. Merci.

0 votes

Non, c'est juste plus verbeux, c'est tout.

1voto

anthoLB29 Points 60

J'essaie d'ajouter Pooling=false comme Ladislav Mrnka l'a dit mais j'ai toujours eu l'erreur.
J'utilise Sql Server Management Studio et même si je ferme toutes les connexions, 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 pourra vous aider

1 votes

Il est probable que vous n'ayez pas fermé toutes les connexions dans Sql Server Management Studio Notez que, comme d'habitude, vous avez TWO les connexions qui y fonctionnent, one for the object explorer AND one for the current query Si vous fermez/interrompez les deux, cela devrait fonctionner.

1 votes

Comme le dit @DrCopyPaste, SSMS est assez agressif pour garder une connexion ouverte à votre BD. Je n'ai pas trouvé de bonne façon de gérer cela, à part fermer l'application.

1 votes

@SteveCooper vous pouvez le faire via script : stackoverflow.com/a/11627/2186023 pas besoin de fermer SSMS, puis après avoir fait votre initialisation/mise à jour, vous pouvez réutiliser ssms, il se souviendra de vos dernières connexions (même si elles sont perdues), il suffit d'appuyer deux fois sur F5, il s'exécutera la deuxième fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X