15

我试图向某人解释为什么数据库连接实现 IDisposable,当我意识到我真的不知道“打开连接”的真正含义时。
所以我的问题是——c# 打开连接时实际上做了什么?

谢谢你。

4

3 回答 3

26

实现连接实际上涉及两个类(实际上更多,但我正在简化)。

其中之一是您在代码中使用的IDbConnection实现(SQLConnectionNpgsqlConnection、等)。OracleConnection另一个是程序集内部的“真实”连接对象,对您的代码不可见。我们暂时称它为“ RealConnection”,尽管它的实际名称因不同的实现而不同(例如在 Npgsql 中,这是我最熟悉的实现的情况,该类被称为NpgsqlConnector)。

当你创建你的IDbConnection时,它没有RealConnection. 任何对数据库做某事的尝试都会失败。当你Open()这样做时,会发生以下情况:

  1. 如果启用RealConnection了池化,并且池中有 a ,则将其加入队列并使其RealConnection成为IDbConnection.
  2. 如果启用池,并且存在的对象总数RealConnection大于最大大小,则抛出异常。
  3. 否则创建一个新的RealConnection. 初始化它,这将涉及打开某种网络连接(例如 TCP/IP)或文件句柄(对于 Access 之类的东西),通过数据库的握手协议(因数据库类型而异)并授权连接。这然后成为RealConnection.IDbConnection

在 上执行的IDbConnection操作将转换为RealConnection对其网络连接(或其他)执行的操作。结果被转换成对象实现IDataReader等等,以便为您的编程提供一致的接口。

如果 aIDataReader是用 创建的CommandBehavior.CloseConnection,那么该数据读取器将获得RealConnection.

当您打电话Close()时,会发生以下情况之一:

  1. 如果池化,并且池未满,则将对象放入队列中以供以后的操作使用。
  2. 否则,RealConnection将执行任何协议定义的过程以结束连接(向数据库发出连接将要关闭的信号)并关闭网络连接等。然后该对象可能会超出范围并可用于垃圾收集。

例外情况是如果CommandBehavior.CloseConnection发生这种情况,在这种情况下,它会触发它Close()或被Dispose()调用。IDataReader

如果你打电话Dispose(),那么会发生同样的事情Close()。不同之处在于它Dispose()被认为是“清理”并且using可以Close()Open().

由于RealConnection对象的使用以及它们被池化的事实,打开和关闭连接从相对较重的东西变为相对较轻的东西。因此,与其让连接长时间保持打开以避免打开它们的开销很重要,不如让它们保持打开的时间越短越好,因为这样会RealConnection为您处理开销,而且速度越快您使用它们,池连接在使用之间共享的效率越高。

另请注意,您已经调用Dispose()过它是可以的(这是一条规则,无论状态如何,调用它应该始终是安全的,即使它已经被调用过)。因此,如果您手动调用,最好将连接放在一个块中,以捕获在调用. 唯一的例外是您实际上希望连接保持打开状态;假设您返回了created with ,在这种情况下不处置,但处置读者。IDbConnectionClose()Dispose()Close()usingClose()IDataReaderCommandBehavior.CloseConnectionIDbConnection

如果您未能处理连接,则不RealConnection会将其返回到池中以供重用,也不会执行其关闭过程。要么池将达到其限制,要么底层连接的数量将增加到破坏性能并阻止创建更多连接的程度。最终RealConnection可能会调用终结器并导致此问题被修复,但终结器只会减少损坏并且不能依赖。(IDbConnection不需要终结器,因为它RealConnection拥有非托管资源和/或需要关闭)。

假设除此之外还有一些其他独特的处置要求也是合理的IDbConnection,即使分析上述内容让您认为它没有必要,它仍然应该被处置(例外是当CommandBehavior.CloseConnection通过所有处置负担时)到IDataReader,但处置该读者同样重要)。

于 2010-10-02T13:29:28.027 回答
4

好问题。

根据我对 SQL 连接的“幕后”工作的(有些有限的知识),涉及到许多步骤,例如:

引擎盖下的步骤

  1. 打开物理套接字/管道(使用给定的驱动程序,例如 ODBC)
  2. 与 SQL Server 握手
  3. 协商的连接字符串/凭据
  4. 事务范围

更不用说连接池,我相信其中涉及某种算法(如果连接字符串与一个已经存在的池匹配,则将连接添加到池中,否则创建新的)

一次性

对于 SQL 连接,我们实现了 IDisposable,以便当我们调用 dispose(通过 using 指令或显式)时,它将连接放回连接池。这与普通的旧 sqlConnection.Close() 形成鲜明对比 - 因为所有这些只是暂时关闭它,但保留该连接以供以后使用。

据我了解,.Close() 关闭与数据库的连接,而 .Dispose() 调用 .Close(),然后释放非托管资源。

牢记这些要点,至少实现 IDisposable 是一种很好的做法。

于 2010-10-02T12:54:30.280 回答
0

添加到上面的答案......关键是,在“打开连接”时可能会分配资源,这将需要比标准垃圾收集更多的时间来恢复,即某种打开的套接字/管道/IPC。Dispose() 方法清除这些。

于 2010-10-02T13:04:24.563 回答