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