Msgstr "Невозможно удалить базу данных, потому что она в данный момент используется". Как исправить? - PullRequest
56 голосов
/ 10 августа 2011

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

63 голосов
/ 10 августа 2011

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

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

39 голосов
/ 10 июля 2014

Я сходил с ума от этого! У меня есть открытое соединение с базой данных внутри 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);
    }
}
18 голосов
/ 14 февраля 2014

Это действительно агрессивный (ре) инициализатор базы данных для 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);

    }
}

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

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

Я также использую трюк Ладислава Мрнки 'Pooling = false', но я не уверен, требуется ли он или просто мера пояса и подтяжки. Это, безусловно, будет способствовать еще большему замедлению теста.

5 голосов
/ 14 марта 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();
    }
}
1 голос
/ 15 декабря 2013

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

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

0 голосов
/ 07 марта 2019

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

Grz John

0 голосов
/ 29 января 2017

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

0 голосов
/ 04 января 2016

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

...