“打开连接”究竟意味着什么?
我试图向某人解释为什么数据库连接实现IDisposable,当我意识到我真的不知道“打开连接”实际意味着什么。
所以我的问题是 – 当c#打开一个连接时,它实际上做了什么?
谢谢。
实际上有两个类涉及实现连接(实际上更多,但我正在简化)。
其中之一是您在代码中使用的IDbConnection
实现( SQLConnection
, NpgsqlConnection
, OracleConnection
等)。 另一个是程序集内部的“真实”连接对象,对代码不可见。 我们现在称之为“ RealConnection
”,虽然它的实际名称因不同的实现而不同(例如在Npgsql中,我最熟悉的是实现,该类称为NpgsqlConnector
)。
创建IDbConnection
,它没有RealConnection
。 任何对数据库执行某些操作的尝试都将失败。 当你Open()
,会发生以下情况:
- 如果启用了池,并且池中存在
RealConnection
,请将其RealConnection
并使其成为IDbConnection
的RealConnection
。 - 如果启用了池,并且存在的
RealConnection
对象总数大于最大大小,则抛出exception。 - 否则创建一个新的
RealConnection
。 初始化它,这将涉及打开某种网络连接(例如TCP / IP)或文件句柄(用于Access之类的东西),通过数据库的协议进行握手(因数据库类型而异)并授权连接。 然后它成为IDbConnection
的RealConnection
。
在IDbConnection
上执行的操作将转换为RealConnection
在其网络连接(或其他)上执行的操作。 结果转化为实现IDataReader
等的对象,以便为您的编程提供一致的接口。
如果使用CommandBehavior.CloseConnection
创建了IDataReader
,则该datareader将获得RealConnection的“所有权”。
当您调用Close()
时,会发生以下某种情况:
- 如果池化,并且池未满,则将该对象放入队列以供稍后的操作使用。
- 否则,
RealConnection
将执行任何协议定义的过程以结束连接(向连接将要关闭的数据库发送信号)并关闭网络连接等。然后,对象可能超出范围并可用于垃圾回收。
例外情况是CommandBehavior.CloseConnection
情况发生,在这种情况下,在触发此情况的IDataReader
上调用Close()
或Dispose()
。
如果你调用Dispose()
然后按照Close()
发生相同的事情。 不同之处在于Dispose()
被认为是“清理”并且可以using
,而Close()
可能在生命的中间使用,然后是稍后的Open()
。
由于RealConnection
对象的使用以及它们被合并的事实,打开和关闭连接从相对较重的东西变为相对较轻的。 因此,保持连接打开很长时间以避免打开它们的开销是很重要的,因此尽可能短的时间让它们保持打开变得很重要,因为RealConnection
为您处理开销,而且更多快速使用它们,在使用之间共享池化连接的效率越高。
还要注意, Dispose()
你已经调用了Close()
Dispose()
的IDbConnection
是可以的(它是一个规则,它应该总是安全地调用Dispose()
,无论状态如何,实际上即使它已被调用)。 因此,如果您手动调用Close()
,那么在using
块中建立连接仍然是好的,以捕获在调用Close()
之前发生exception的情况。 唯一的例外是你真正希望连接保持开放的地方; 假设您正在返回使用CommandBehavior.CloseConnection
创建的IDataReader
,在这种情况下,您不会丢弃IDbConnection
,但会丢弃读取器。
如果您未能处置连接,则RealConnection
将不会返回池中以供重用,或者执行其关闭过程。 池将达到其限制,或者底层连接的数量将增加到破坏性能的程度并阻止更多的创建。 最终可能会调用RealConnection上的RealConnection
器并导致其被修复,但最终确定只会减少损坏并且无法依赖。 ( IDbConnection
不需要终结器,因为它是保存非托管资源和/或需要关闭的RealConnection
)。
假设除此之外还有一些其他的IDbConnection
实现唯一的处理要求,即使分析上述内容导致您认为不必要,也应该将其处理掉(例外情况是CommandBehavior.CloseConnection
将所有处理负担传递给IDataReader
,但是配置读取器同样重要。
好问题。
从我对SQL连接的“底层”工作(有点有限的知识),涉及许多步骤,例如:
引擎盖下的步骤
- 打开物理套接字/管道(使用给定的驱动程序,例如ODBC)
- 与SQL Server的握手
- 协商的连接字符串/凭据
- 交易范围
更不用说连接池了,我相信会涉及某种算法(如果连接字符串与已存在的池匹配,则连接被添加到池中,否则会创建新的连接)
IDiposable
关于SQL Connections,我们实现了IDisposable,这样当我们调用dispose(通过using指令或表达式)时,它会将连接放回连接池。 这与普通的旧sqlConnection.Close()形成鲜明对比 – 因为所有这一切都暂时关闭它,但保留该连接供以后使用。
根据我的理解,.Close()关闭与数据库的连接,而.Dispose()调用.Close(), 然后释放非托管资源。
记住这些要点,至少实现IDisposable是一种好习惯。
添加上面的答案……关键是,在“打开连接”时,可能会分配资源,这将需要超过标准的垃圾收集来恢复,即某些开放的套接字/管道/ IPC。 Dispose()方法清除了这些。