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