Невозможно удалить базу данных, потому что она в настоящее время используется. Как исправить?

Имея этот простой код, я получаю сообщение «Невозможно удалить базу данных« 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; }

Как заставить мой код удалить базу данных? Спасибо


comment
CTP5 мертв. Текущая версия - EF 4.1.   -  person Ladislav Mrnka    schedule 10.08.2011


Ответы (8)


Проблема в том, что ваше приложение, вероятно, все еще имеет какое-то соединение с базой данных (или другое приложение также поддерживает соединение). База данных не может быть удалена там, где есть другое открытое соединение. Первую проблему, вероятно, можно решить, отключив пул соединений (добавьте Pooling=false в строку подключения) или очистив пул перед удалением базы данных (путем вызова SqlConnection.ClearAllPools()).

Обе проблемы могут быть решены путем принудительного удаления базы данных, но для этого вам понадобится настраиваемый инициализатор базы данных, в котором вы переключаете базу данных в однопользовательский режим, а затем удаляете ее. Вот пример как этого добиться.

person Ladislav Mrnka    schedule 10.08.2011
comment
Pooling = false сделал работу. Спасибо! - person YMC; 10.08.2011
comment
@LadislavMrnka Как насчет того, что у меня pooling = false, и я уже устанавливаю single_user, но все равно получаю это сообщение об ошибке? - person ashes999; 07.12.2011

Я сходил с ума от этого! У меня есть открытое соединение с базой данных внутри SQL Server Management Studio (SSMS) и открыт запрос таблицы, чтобы увидеть результат некоторых модульных тестов. При повторном запуске тестов внутри Visual Studio я хочу, чтобы drop база данных всегда была ДАЖЕ, ЕСЛИ, соединение открыто в SSMS.

Вот окончательный способ избавиться от Cannot drop database because it is currently in use:

Инициализация базы данных Entity Framework

Уловка состоит в том, чтобы переопределить метод 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);
    }
}
person Leniel Maccaferri    schedule 10.07.2014
comment
Это был самый простой и понятный способ отправить пользователей на автоматические тесты. - person moribvndvs; 13.11.2014
comment
Добавьте IF EXISTS, и все будет идеально. - person ericosg; 03.11.2015
comment
@ericosg Как использовать IF EXISTS? - person Kiquenet; 03.06.2017
comment
Работает для файлов mdf с использованием Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\wingtiptoys.mdf;Integrated Security=True? - person Kiquenet; 03.06.2017
comment
@Kiquenet вроде этого stackoverflow.com/questions/679000/ - person ericosg; 05.06.2017

Это действительно агрессивный (ре) инициализатор базы данных для EF code-first с миграциями; используйте его на свой страх и риск, но, похоже, для меня он довольно часто повторяется. Будет;

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

    }
}

Используйте это так;

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

Я также использую уловку Ладислава Мрнки «Объединение = ложь», но я не уверен, требуется ли это или просто мера с поясом и подтяжками. Это, безусловно, еще больше замедлит тест.

person Steve Cooper    schedule 14.02.2014

Ни одно из этих решений не помогло мне. В итоге я написал метод расширения, который работает:

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();
    }
}
person Chris McKenzie    schedule 13.03.2014
comment
Вместо использования database.Connection.CreateCommand() мы можем использовать dbContext.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, sqlString). В противном случае это хорошее решение. - person niaher; 03.11.2015
comment
Помогите мне понять, пожалуйста - в чем проблема с database.Connection.CreateCommand()? Никаких проблем не было. Спасибо! - person Chris McKenzie; 03.11.2015
comment
Нет, просто подробней и все. - person niaher; 05.11.2015
comment
Как это использовать ? Database.SetInitializer(new EventDatabaseInitializer());? - person Kiquenet; 03.06.2017

Я пытаюсь добавить Pooling=false, как сказал Ладислав Мрнка, но всегда получаю ошибку.
Я использую Sql Server Management Studio, и даже если я закрываю все соединения, я получаю сообщение об ошибке.

Если я закрою Sql Server Management Studio, база данных будет удалена :)
Надеюсь, это поможет.

person anthoLB29    schedule 15.12.2013
comment
скорее всего, вы не закрыли все соединения в Sql Server Management Studio, обратите внимание, что обычно у вас есть TWO соединения, one for the object explorer AND one for the current query если вы закроете / переключите оба из них, он должен работать - person DrCopyPaste; 28.01.2014
comment
Как говорит @DrCopyPaste, SSMS довольно агрессивно поддерживает соединение с вашей БД. Я не нашел хорошего способа справиться с этим, кроме закрытия приложения. - person Steve Cooper; 13.02.2014
comment
@SteveCooper, вы можете сделать это с помощью скрипта: stackoverflow.com/a/11627/2186023 не нужно выключать SSMS, затем после того, как вы выполнили инициализацию / обновление, вы можете повторно использовать ssms, он запомнит ваши последние подключения (хотя они потеряны), просто дважды нажмите F5, он выполнится во второй раз - person DrCopyPaste; 13.02.2014

У меня такая же ошибка. В моем случае я просто закрыл соединение с базой данных, а затем снова подключился, как только в моем случае была добавлена ​​новая модель и был построен новый контроллер. Однако это очень простое решение и не рекомендуется для всех сценариев, если вы хотите сохранить свои данные.

person user5504242    schedule 04.01.2016

У меня тогда была такая же проблема. Оказывается, решение состоит в том, чтобы закрыть соединение на вкладке Server Explorer в Visual Studio. Так что, возможно, вы могли бы проверить, открыто ли соединение в обозревателе серверов.

person Sambalado    schedule 29.01.2017

Это просто, потому что вы все еще где-то используете ту же базу данных или соединение все еще открыто. Так что просто сначала выполните «USE master» (если он существует, но обычно есть), а затем отбросьте другой db. Это всегда должно работать!

Грз Джон

person John Schellingerhout    schedule 07.03.2019