Что означает "открытие соединения"?

Я пытался объяснить кому-то, почему подключения к базе данных реализуют IDisposable, когда я понял, что я действительно не знаю, что означает "открытие соединения". Поэтому мой вопрос: что делает С# практически при открытии соединения?

Спасибо.

Ответ 1

Есть фактически два класса, участвующих в реализации соединения (на самом деле больше, но я упрощаю).

Один из них - реализация IDbConnection (SQLConnection, NpgsqlConnection, OracleConnection и т.д.), которые вы используете в своем коде. Другой - это "реальный" объект соединения, который является внутренним для сборки и не отображается для вашего кода. На данный момент мы назовем это "RealConnection", хотя его фактическое имя отличается от разных реализаций (например, в Npgsql, в том случае, когда я больше всего знаком с реализацией, класс называется NpgsqlConnector).

Когда вы создаете свой IDbConnection, он не имеет RealConnection. Любая попытка сделать что-то с базой данных не удастся. Когда вы Open(), тогда происходит следующее:

  • Если пул включен, а в пуле есть RealConnection, декайте его и сделайте его RealConnection для IDbConnection.
  • Если объединение включено, а общее число существующих объектов RealConnection больше максимального размера, генерируйте исключение.
  • В противном случае создайте новый RealConnection. Инициализируйте его, что приведет к открытию какого-то сетевого соединения (например, TCP/IP) или дескриптора файла (для чего-то вроде Access), пройдите протокол базы данных для дрожания рук (зависит от типа базы данных) и авторизуйте соединение. Тогда это будет RealConnection для IDbConnection.

Операции, выполняемые на IDbConnection, преобразуются в операции, которые RealConnection выполняет в своем сетевом соединении (или что-то еще). Результаты преобразуются в объекты, реализующие IDataReader и т.д., Чтобы обеспечить согласованный интерфейс для вашего программирования.

Если a IDataReader был создан с помощью CommandBehavior.CloseConnection, тогда этот datareader получает "собственность" RealConnection.

Когда вы вызываете Close(), происходит одно из следующих событий:

  • Если пул пула не заполнен, тогда объект помещается в очередь для использования с более поздними операциями.
  • В противном случае RealConnection будет выполнять любые протокольные процедуры для прекращения соединения (сигнализация в базе данных, что соединение будет закрыто) и закрывает сетевое соединение и т.д. Затем объект может выпасть из области действия и становятся доступными для сбора мусора.

Исключение было бы, если бы произошел случай CommandBehavior.CloseConnection, и в этом случае он вызывал Close() или Dispose() в IDataReader, который запускает это.

Если вы вызываете Dispose(), то происходит то же самое, что и в Close(). Разница в том, что Dispose() считается "очисткой" и может работать с using, а Close() может использоваться в середине срока службы, а затем более поздним Open().

Из-за использования объекта RealConnection и того факта, что они объединены, открытие и закрытие соединений изменяется от того, что относительно тяжело относительно относительно светло. Следовательно, вместо того, чтобы важно долгое время открывать связи, чтобы избежать накладных расходов при их открытии, важно сохранить их как можно короче, поскольку RealConnection имеет дело с накладными расходами для вас и чем быстрее вы их используете, тем эффективнее объединенные соединения получат совместное использование.

Заметим также, что все в порядке Dispose() a IDbConnection, которое вы уже вызывали Close() on (это правило, что всегда должно быть безопасно вызывать Dispose(), независимо от состояния, даже если оно уже было вызвано). Следовательно, если вы вручную вызывали Close(), было бы неплохо иметь соединение в блоке using, чтобы улавливать случаи, когда исключения происходят до вызова Close(). Единственное исключение - это то, где вы действительно хотите, чтобы соединение оставалось открытым; скажем, вы возвращали IDataReader, созданный с помощью CommandBehavior.CloseConnection, и в этом случае вы не располагаете IDbConnection, а do удаляете читателя.

Если вам не удастся установить соединение, то RealConnection не будет возвращен в пул для повторного использования или продолжит его процедуру останова. Либо пул достигнет своего предела, либо количество базовых подключений увеличится до уровня повреждения производительности и блокирует больше от создания. В конце концов финалист на RealConnection может быть вызван и приведет к его исправлению, но завершение только уменьшает ущерб и от этого не может зависеть. (IDbConnection не нуждается в финализаторе, так как он RealConnection, который содержит неуправляемый ресурс и/или нуждается в выключении).

Также разумно предположить, что существует иное требование к удалению, уникальное для реализации IDbConnection, кроме этого, и его все равно следует удалять, даже если анализ вышеизложенного приводит к тому, что вы считаете его не необходимым (исключение когда CommandBehavior.CloseConnection передает всю нагрузку на утилиту IDataReader, но тогда так же важно распоряжаться этим читателем).

Ответ 2

Хороший вопрос.

Из моего (несколько ограниченного знания) работы "под капотом" SQL-соединения задействованы многие шаги, такие как:

Шаги под капюшоном

  • Физический сокет/труба открывается (с использованием заданных драйверов, например, ODBC)
  • Рукопожатие с SQL Server
  • Строка подключения/учетные данные, согласованные
  • Обзор транзакций

Не говоря уже о пуле соединений, я считаю, что есть какой-то алогритм (если строка соединения соответствует одному для уже существующего пула, соединение добавляется в пул, в противном случае создается новый)

IDiposable

Что касается SQL Connections, мы реализуем IDisposable, чтобы при вызове dispose (либо с помощью директивы using, либо из-за эксплицитности) он помещает соединение обратно в пул соединений. Это резко контрастирует с простым старым sqlConnection.Close() - поскольку все это временно закрывает его, но резервирует это соединение для последующего использования.

Из моего понимания,.Close() закрывает соединение с базой данных, тогда как .Dispose() вызывает .Close() и , а затем освобождает неуправляемые ресурсы.

Эти точки в виду, по крайней мере, это хорошая практика для реализации IDisposable.

Ответ 3

Добавление к ответам выше... Ключ состоит в том, что при открытии соединения могут быть выделены ресурсы, которые могут потребовать больше, чем стандартная сборка мусора для восстановления, а именно open socket/pipe/IPC somekind. Метод Dispose() очищает их.