SqlConnection/SqlCommand сохраняет базу данных в использовании после закрытия и удаления

Я пытаюсь создать временную базу данных с целью интеграционного тестирования в Xunit, но когда я пытаюсь удалить временную базу данных, я получаю сообщение об ошибке:

Невозможно удалить базу данных "TempDatabase_[numbers]", так как она в настоящее время используется.

Простое закрытие и удаление команды и соединения, похоже, не помогает.

Это урезанная версия моего теста, который не работает:

using System;
using System.Data.SqlClient;
using Xunit;

namespace Test
{
    public class Test_Raw_Spec
    {
        [Fact]
        public void PerformWorkInTemporaryDatabase()
        {
            string connectionStringTemplate = "Data Source=SQLEXPRESS;Initial Catalog = {0};Integrated Security=SSPI;Connection Timeout=10";
            int dbNum = (new Random()).Next() % 1000000;
            int tblNum = (new Random()).Next() % 1000000;

            string nameTempDb = $"TempDatabase_{dbNum}";
            string nameTempTable = $"TempTable_{tblNum}";

            var sqlConnection1 = new SqlConnection(string.Format(connectionStringTemplate, "master"));
            var sqlCommand1 = new SqlCommand($"CREATE DATABASE {nameTempDb}", sqlConnection1);
            sqlConnection1.Open();
            sqlCommand1.ExecuteNonQuery();
            sqlCommand1.Dispose();
            sqlConnection1.Close();
            sqlConnection1.Dispose();

            var sqlConnection2 = new SqlConnection(string.Format(connectionStringTemplate, nameTempDb));
            var sqlCommand2 = new SqlCommand($"CREATE TABLE {nameTempTable}(id int)", sqlConnection2);
            sqlConnection2.Open();
            sqlCommand2.ExecuteNonQuery();
            sqlCommand2.Dispose();
            sqlConnection2.Close();
            sqlConnection2.Dispose();

            var sqlConnection3 = new SqlConnection(string.Format(connectionStringTemplate, "master"));
            var sqlCommand3 = new SqlCommand($"DROP DATABASE {nameTempDb}", sqlConnection3);
            sqlConnection3.Open();
            sqlCommand3.ExecuteNonQuery();
            sqlCommand3.Dispose();
            sqlConnection3.Close();
            sqlConnection3.Dispose();
        }
    }
}
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
3
0
721
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы пробовали немного подождать, прежде чем удалить БД? Вполне может быть, что соединение не успело вовремя закрыться.

Также вам не нужно закрывать соединение, вызов dispose все равно сделает все за вас. Плюс лучше всего сделать это в операторе using

using(var sqlConnection1 = new SqlConnection(string.Format(connectionStringTemplate, "master")))
{
//do your stuff here
}

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

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

Добавление 120-секундной задержки между соединениями 2 и 3 (System.Threading.Thread.Sleep(2 * 60 * 1000);), похоже, не помогает. Мне нравятся ваши другие предложения, и я реализую их, как только получу базовый уровень.

VeeTheSecond 20.12.2020 21:05

Попробуйте вызвать sqlConnection2.ClearAllPools() перед удалением базы данных

coolblue2000 20.12.2020 21:08
Ответ принят как подходящий

Это из-за пула соединений. Когда вы закрываете и уничтожаете соединение, оно возвращается в пул, готовое к повторному использованию, а не уничтожается. Создание и удаление соединений снова и снова — очень затратный процесс, поэтому пулы соединений используются для повышения общей производительности вашего приложения. Соединения уничтожаются, когда они завершены (когда пул перезапускается или перезапускается, например, когда ваше приложение запускается или закрывается).

Кроме того, вы могли бы более эффективно использовать команды и соединения. Вы можете изменить текст команды, если она выполнена. Если вы не хотите этого делать, вы можете хотя бы повторно использовать соединение:

private void Execute()
{
    using (var connection = new SqlConnection("."))
    {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
            command.CommandText = "CREATE DATABASE [test]";
            command.ExecuteNonQuery();

            connection.ChangeDatabase("test");
            command.CommandText = "CREATE TABLE [dbo].[MyTable] (id int)";
            command.ExecuteNonQuery();
            
            // you must change your db context to drop the database
            connection.ChangeDatabase("master");
            command.CommandText = "DROP DATABASE [test]";
            command.ExecuteNonQuery();
        }
    }
}

Пул реализован в библиотеке SqlClient. Вы получаете один пул для каждой уникальной строки подключения и не связаны с использованием разработчиками инструкции «using».

World Wide DBA 20.12.2020 21:11

Да, вы правы. Я понял только после того, как набрал комментарий и удалил его...

coolblue2000 20.12.2020 21:14
ALTER DATABASE [test] SET SINGLE_USER WITH ROLLBACK AFTER 5 заставит все остальные соединения отключиться от базы данных через 5 секунд (например, другие соединения в пуле).
Richard 20.12.2020 21:34

Я обнаружил, что вы также можете добавить Pooling=false к своей строке подключения, что в конечном итоге также очистит пул соединений (см. stackoverflow.com/questions/7004701/…). Поскольку предполагается, что интеграционное тестирование является меньшим подмножеством всех ваших тестов, это также было бы жизнеспособной альтернативой для меня.

VeeTheSecond 21.12.2020 03:15

Другие вопросы по теме