60

Having this simple code I get "Cannot drop database "test_db" because it is currently in use" (CleanUp method) as I run it.

[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 has one property like this

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

How can force my code to remove database? Thanks

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
YMC
  • 4,925
  • 7
  • 53
  • 83

8 Answers8

71

The problem is that your application probably still holds some connection to the database (or another application holds connection as well). Database cannot be deleted where there is any other opened connection. The first problem can be probably solved by turning connection pooling off (add Pooling=false to your connection string) or clear the pool before you delete the database (by calling SqlConnection.ClearAllPools()).

Both problems can be solved by forcing database to delete but for that you need custom database initializer where you switch the database to single user mode and after that delete it. Here is some example how to achieve that.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
43

I was going crazy with this! I have an open database connection inside SQL Server Management Studio (SSMS) and a table query open to see the result of some unit tests. When re-running the tests inside Visual Studio I want it to drop the database always EVEN IF the connection is open in SSMS.

Here's the definitive way to get rid of Cannot drop database because it is currently in use:

Entity Framework Database Initialization

The trick is to override InitializeDatabase method inside the custom Initializer.

Copied relevant part here for the sake of good DUPLICATION... :)

If the database already exist, you may stumble into the case of having an error. The exception “Cannot drop database because it is currently in use” can raise. This problem occurs when an active connection remains connected to the database that it is in the process of being deleted. A trick is to override the InitializeDatabase method and to alter the database. This tell the database to close all connection and if a transaction is open to rollback this one.

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);
    }
}
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
18

This is a really aggressive database (re)initializer for EF code-first with migrations; use it at your peril but it seems to run pretty repeatably for me. It will;

  1. Forcibly disconnect any other clients from the DB
  2. Delete the DB.
  3. Rebuild the DB with migrations and runs the Seed method
  4. Take ages! (watch the timeout limit for your test framework; a default 60 second timeout might not be enough)

Here's the class;

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

    }
}

Use it like this;

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

I also use Ladislav Mrnka's 'Pooling=false' trick, but I'm not sure if it's required or just a belt-and-braces measure. It'll certainly contribute to slowing down the test more.

Steve Cooper
  • 20,542
  • 15
  • 71
  • 88
5

None of those solutions worked for me. I ended up writing an extension method that works:

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();
    }
}
Chris McKenzie
  • 3,681
  • 3
  • 27
  • 36
  • Instead of using `database.Connection.CreateCommand()`, we can use `dbContext.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlString)`. Otherwise it's a nice solution. – niaher Nov 03 '15 at 14:57
  • Help my understanding please - what's the issue with `database.Connection.CreateCommand()` ? I haven't had any problems. Thanks! – Chris McKenzie Nov 03 '15 at 18:11
  • None, it's just more verbose that's all. – niaher Nov 05 '15 at 16:35
  • How to use it ? `Database.SetInitializer(new EventDatabaseInitializer());` ? – Kiquenet Jun 03 '17 at 13:30
2

I try adding Pooling=false like Ladislav Mrnka said but always got the error.
I'm using Sql Server Management Studio and even if I close all the connection, I get the error.

If I close Sql Server Management Studio then the Database is deleted :)
Hope this can helps

anthoLB29
  • 105
  • 1
  • 9
  • 1
    most likely you did not close all connections in `Sql Server Management Studio`, note that per usual you have `TWO` connections running there, `one for the object explorer AND one for the current query` if you close/switch both of them it should work – DrCopyPaste Jan 28 '14 at 12:46
  • 1
    As @DrCopyPaste says, SSMS is pretty aggressive about keeping a connection open to your DB. I've not found a good way to handle it except closing the app. – Steve Cooper Feb 13 '14 at 11:05
  • 1
    @SteveCooper you can do it via script: http://stackoverflow.com/a/11627/2186023 no need to shut down SSMS, then after you got your initialization/update done, you can reuse ssms, it will remember your last connections (though they are lost), just hit F5 twice, it will execute the second time – DrCopyPaste Feb 13 '14 at 11:19
  • @anthoLB29 An oldy but still a goody, all the way back from 2013! I just spent 10 minutes resetting my password to give you +1 on this. I smirched when I read it, because it sounded logical yet too easy and too good to be true. Well, it wasn't! Goodbye Entity Framework `DropCreateDatabaseAlways` issues... – AlphaG33k Mar 16 '22 at 01:01
0

I got the same error. In my case, I just closed the connection to the database and then re-connected once the in my case the new model was added and a new controller was scaffolded. That is however a very simple solution and not recommended for all scenarios if you want to keep your data.

0

I got the same problem back then. Turns out the solution is to close the connection in Server Explorer tab in Visual Studio. So maybe you could check whether the connection is still open in the Server Explorer.

Sambalado
  • 33
  • 7
-1

Its simple because u're still using the same db somewhere, or a connection is still open. So just execute "USE master" first (if exist, but usually is) and then drop the other db. This always should work!

Grz John