Я пытался объяснить кому-то, почему подключения к базе данных реализуют IDisposable, когда я понял, что я действительно не знаю, что означает "открытие соединения". Поэтому мой вопрос: что делает С# практически при открытии соединения?
Спасибо.
Я пытался объяснить кому-то, почему подключения к базе данных реализуют IDisposable, когда я понял, что я действительно не знаю, что означает "открытие соединения". Поэтому мой вопрос: что делает С# практически при открытии соединения?
Спасибо.
Есть фактически два класса, участвующих в реализации соединения (на самом деле больше, но я упрощаю).
Один из них - реализация 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
, но тогда так же важно распоряжаться этим читателем).
Хороший вопрос.
Из моего (несколько ограниченного знания) работы "под капотом" SQL-соединения задействованы многие шаги, такие как:
Шаги под капюшоном
Не говоря уже о пуле соединений, я считаю, что есть какой-то алогритм (если строка соединения соответствует одному для уже существующего пула, соединение добавляется в пул, в противном случае создается новый)
IDiposable
Что касается SQL Connections, мы реализуем IDisposable, чтобы при вызове dispose (либо с помощью директивы using, либо из-за эксплицитности) он помещает соединение обратно в пул соединений. Это резко контрастирует с простым старым sqlConnection.Close() - поскольку все это временно закрывает его, но резервирует это соединение для последующего использования.
Из моего понимания,.Close() закрывает соединение с базой данных, тогда как .Dispose() вызывает .Close() и , а затем освобождает неуправляемые ресурсы.
Эти точки в виду, по крайней мере, это хорошая практика для реализации IDisposable.
Добавление к ответам выше... Ключ состоит в том, что при открытии соединения могут быть выделены ресурсы, которые могут потребовать больше, чем стандартная сборка мусора для восстановления, а именно open socket/pipe/IPC somekind. Метод Dispose() очищает их.