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

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

Спасибо.

3 ответа

Решение

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

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

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

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

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

Если IDataReader был создан с CommandBehavior.CloseConnection то этот читатель получает "право собственности" на RealConnection,

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

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

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

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

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

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

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

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

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

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

Шаги под капотом

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

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

IDiposable

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

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

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

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

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