“无法删除数据库,因为它当前正在使用中”。怎么修?

时间:2011-08-10 00:41:23

标签: database unit-testing entity-framework entity-framework-4.1 database-connection

有了这个简单的代码,我得到“无法删除数据库”test_db“因为它当前正在使用”(CleanUp方法),因为我运行它。

[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有一个像这样的属性

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

如何强制我的代码删除数据库? 感谢

8 个答案:

答案 0 :(得分:63)

问题是你的应用程序可能仍然保持与数据库的某些连接(或者另一个应用程序也保持连接)。在有任何其他打开的连接的情况下,无法删除数据库。可以通过关闭连接池(将Pooling=false添加到连接字符串)或在删除数据库之前清除池(通过调用SqlConnection.ClearAllPools())来解决第一个问题。

这两个问题都可以通过强制数据库删除来解决,但为此需要自定义数据库初始化程序,您可以将数据库切换到单用户模式,然后删除它。 Here是如何实现这一目标的一些例子。

答案 1 :(得分:38)

我为此疯了!我在SQL Server Management Studio (SSMS)内有一个开放的数据库连接,并打开一个表查询以查看某些单元测试的结果。在Visual Studio中重新运行测试时,我希望它drop数据库始终 即使 ,SSMS中的连接已打开。

这是摆脱Cannot drop database because it is currently in use的明确方法:

Entity Framework Database Initialization

诀窍是覆盖自定义InitializeDatabase内的Initializer方法。

为了good DUPLICATION 而复制相关部分......:)

  

如果数据库已经存在,您可能会遇到困难的情况   一个错误。例外情况“无法删除数据库,因为它当前是   在使用“可以提高。活动连接时会发生此问题   仍然连接到正在进行的数据库   删除。一个技巧是覆盖InitializeDatabase方法和   改变数据库。这告诉数据库关闭所有连接和   如果某个事务处于打开状态以回滚此事务。

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

答案 2 :(得分:18)

这是一个非常积极的数据库(重新)初始化程序,用于EF代码优先迁移;使用它会带来危险,但它似乎对我来说非常重复。它会;

  1. 强制断开任何其他客户端与数据库的连接
  2. 删除数据库。
  3. 使用迁移重建数据库并运行Seed方法
  4. 多岁! (观察测试框架的超时限制;默认的60秒超时可能不够)
  5. 这是班级;

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

    像这样使用它;

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

    我也使用Ladislav Mrnka&#39; Pooling = false&#39;诀窍,但我不确定它是否需要或只是一个腰带和大括号的措施。它肯定有助于减慢测试速度。

答案 3 :(得分:5)

这些解决方案都不适合我。我最终写了一个有效的扩展方法:

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

答案 4 :(得分:1)

我尝试像Ladislav Mrnka那样添加Pooling=false,但总是得到错误 我正在使用 Sql Server Management Studio ,即使我关闭了所有连接,也会收到错误。

如果我关闭 Sql Server Management Studio ,则删除数据库:)
希望这可以帮助

答案 5 :(得分:0)

我得到了同样的错误。在我的情况下,我只是关闭了与数据库的连接,然后在我的情况下重新连接新模型,并且新的控制器是脚手架。然而,这是一个非常简单的解决方案,如果您想保留数据,则不建议用于所有情况。

答案 6 :(得分:0)

那时我遇到了同样的问题。原来解决方案是在Visual Studio的“服务器资源管理器”选项卡中关闭连接。因此,您可以检查服务器资源管理器中的连接是否仍然打开。

答案 7 :(得分:-1)

这很简单,因为您在某处仍在使用相同的数据库,或者连接仍处于打开状态。 因此,只需先执行“ USE master”(如果存在,但通常是),然后删除其他数据库。这总是应该起作用!

Grz John