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

YMC*_*YMC 58 database unit-testing entity-framework database-connection entity-framework-4.1

有这个简单的代码,我得到"不能删除数据库"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);
    }
}
Run Code Online (Sandbox Code Playgroud)

DataContext有一个这样的属性

 public DbSet<Client> Clients { get; set; }
Run Code Online (Sandbox Code Playgroud)

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

Lad*_*nka 65

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

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

  • Pooling = false完成了这项工作.谢谢! (4认同)
  • @LadislavMrnka如果我有pooling = false并且我已经设置了single_user,但是仍然收到此错误消息怎么样? (2认同)

Len*_*rri 39

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

这是摆脱的最终方式Cannot drop database because it is currently in use:

实体框架数据库初始化

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

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

如果数据库已经存在,您可能会遇到错误的情况."无法删除数据库,因为它当前正在使用"的异常可以引发.当活动连接保持连接到数据库时,它正在被删除的过程中会发生此问题.一个技巧是覆盖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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 添加`IF EXISTS`并且它是完美的. (2认同)

Ste*_*per 18

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

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

这是班级;

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

    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它;

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我也使用Ladislav Mrnka的'Pooling = false'技巧,但我不确定它是否需要或只是一个带和括号的措施.它肯定会有助于减慢测试速度.


Chr*_*zie 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();
    }
}
Run Code Online (Sandbox Code Playgroud)