Pergunta

Eu estava tentando explicar a alguém porque conexões de banco de dados Implemente o IDisposable, quando percebi que realmente não sei o que realmente significa "abrir uma conexão".
Então, minha pergunta é - o que C# praticamente faz quando abre uma conexão?

Obrigada.

Foi útil?

Solução

Na verdade, existem duas classes envolvidas na implementação de uma conexão (na verdade, mais, mas estou simplificando).

Um deles é o IDbConnection implementação (SQLConnection, NpgsqlConnection, OracleConnection, etc.) que você usa em seu código. O outro é um objeto de conexão "real" que é interno à montagem e não é visível ao seu código. Vamos chamar isso de "RealConnection"Por enquanto, embora seu nome real seja diferente de diferentes implementações (por exemplo, no NPGSQL, que é o caso em que estou mais familiarizado com a implementação, a classe é chamada NpgsqlConnector).

Quando você cria o seu IDbConnection, não tem um RealConnection. Qualquer tentativa de fazer algo com o banco de dados falhará. Quando você Open() então o seguinte acontece:

  1. Se o pool estiver ativado, e há um RealConnection na piscina, deque -a e faça com que seja o RealConnection para o IDbConnection.
  2. Se o pool estiver ativado e o número total de RealConnection Objetos existentes são maiores que o tamanho máximo, faça uma exceção.
  3. Caso contrário, crie um novo RealConnection. Inicialize-o, que envolverá a abertura de algum tipo de conexão de rede (por exemplo, TCP/IP) ou identificador de arquivo (para algo como acesso), passe pelo protocolo do banco de dados para a baleia das mãos (varia com o tipo de banco de dados) e autorize a conexão. Isso então se torna o RealConnection para o IDbConnection.

Operações realizadas no IDbConnection são transformados em operações o RealConnection faz em sua conexão de rede (ou qualquer outra coisa). Os resultados são transformados em objetos implementando IDataReader E assim por diante, para fornecer uma interface consistente para sua programação.

Se um IDataReader foi criado com CommandBehavior.CloseConnection, então esse DataReader obtém "propriedade" do RealConnection.

Quando Você ligar Close() Então um dos seguintes itens acontece:

  1. Se o pool, e se o pool não estiver cheio, o objeto será colocado na fila para uso com operações posteriores.
  2. Caso contrário, o RealConnection Realizará quaisquer procedimentos definidos por protocolo para encerrar a conexão (sinalização ao banco de dados que a conexão será desligada) e fecha a conexão de rede etc. O objeto pode cair do escopo e ficar disponível para coleta de lixo.

A exceção seria se o CommandBehavior.CloseConnection Caso aconteceu, nesse caso, é Close() ou Dispose() sendo chamado no IDataReader Isso desencadeia isso.

Se você ligar Dispose() Então a mesma coisa acontece como Close(). A diferença é que Dispose() é considerado como "limpeza" e pode trabalhar com using, enquanto Close() pode ser usado no meio da vida e seguido por um posterior Open().

Por causa do uso do RealConnection Objeto e o fato de serem reunidos, abrindo e fechando as conexões mudam de algo relativamente pesado para relativamente leve. Portanto, em vez de ser importante manter as conexões abertas por um longo tempo para evitar a sobrecarga de abri -las, torna -se importante mantê -las abertas o mais curto possível, uma vez que o RealConnection Lida com a sobrecarga para você e, quanto mais rapidamente você as usa, mais eficientemente as conexões agrupadas são compartilhadas entre os usos.

Observe também que não há problema em Dispose() um IDbConnection que você já ligou Close() em (é uma regra que sempre deve ser seguro ligar Dispose(), qualquer que seja o estado, de fato, mesmo que já fosse chamado). Portanto, se você estava ligando manualmente Close() ainda seria bom ter a conexão em um using Block, para capturar casos em que as exceções acontecem antes da chamada para Close(). A única exceção é onde você realmente deseja que a conexão fique aberta; diga que você estava devolvendo um IDataReader criado com CommandBehavior.CloseConnection, nesse caso, você não descarta o IDbConnection, mas Faz descarte o leitor.

Você deve deixar de descartar a conexão, então o RealConnection não será devolvido ao pool para reutilização ou passar pelo procedimento de desligamento. O pool atingirá seu limite ou o número de conexões subjacentes aumentará ao ponto de prejudicar o desempenho e o bloqueio mais por ser criado. Eventualmente o final do final RealConnection pode ser chamado e levar a isso ser fixo, mas a finalização apenas reduz o dano e não pode depender. (O IDbConnection não precisa de um final, pois é o RealConnection Isso mantém o recurso não gerenciado e/ou precisa fazer o desligamento).

Também é razoável supor que existe algum outro requisito para o descarte exclusivo da implementação do IDbConnection além disso, e ainda deve ser descartado, mesmo que a análise acima o leve a acreditar que não é necessário (a exceção é quando CommandBehavior.CloseConnection passa toda a carga de descarte para o IDataReader, mas então é igualmente importante descartar esse leitor).

Outras dicas

Boa pergunta.

Do meu (um pouco limitado de conhecimento) do trabalho "sob a calça" de uma conexão SQL, muitas etapas estão envolvidas, como:

Os degraus sob o capô

  1. Soquete físico/tubo é aberto (usando drivers determinados, por exemplo, ODBC)
  2. Handshake com SQL Server
  3. String de conexão/credenciais negociadas
  4. Escopo de transação

Sem mencionar o pool de conexões, acredito que existe algum tipo de alogritmo envolvido (se a string de conexão corresponde a uma para um pool já existente, a conexão será adicionada ao pool, caso contrário, a nova será criada)

Idipossável

No que diz respeito às conexões SQL, implementamos o IDisposable para que, quando chamamos Dispose (através da Diretiva Usando ou Explicidade), ele coloca a conexão novamente no pool de conexão. Isso contrasta fortemente apenas com o antigo sqlConnection.close () - como tudo isso é fechado temporariamente, mas reserva essa conexão para uso posterior.

Do meu entendimento, .close () fecha a conexão com o banco de dados, enquanto .dispose () chama .close () e então libera recursos não gerenciados.

Esses pontos em mente, no mínimo, é uma boa prática implementar o IDisposable.

Adicionando as respostas acima ... A chave é que, ao "abrir a conexão", os recursos podem ser alocados que levarão mais do que a coleção de lixo padrão para se recuperar, a saber, um soquete/tubo/ipc aberto de alguma coisa. O método dispete () os limpa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top