“打开连接”究竟意味着什么?

时间:2010-10-02 12:47:26

标签: c# idisposable resource-management idbconnection

我试图向某人解释为什么数据库连接实施IDisposable,当我意识到我真的不知道“打开连接”实际意味着什么。
所以我的问题是 - 当它打开连接时,c#几乎可以做什么?

谢谢。

3 个答案:

答案 0 :(得分:22)

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

其中之一是您在代码中使用的IDbConnection实施(SQLConnectionNpgsqlConnectionOracleConnection等)。另一个是程序集内部的“真实”连接对象,对代码不可见。我们现在称之为“RealConnection”,虽然它的实际名称因实现的不同而不同(例如在Npgsql中,我最熟悉的是实现,该类称为{{1} })。

创建NpgsqlConnector时,它没有IDbConnection。任何对数据库执行某些操作的尝试都将失败。当你RealConnection时,会发生以下情况:

  1. 如果启用了池,并且池中有Open(),请将其解除并使其成为RealConnection的{​​{1}}。
  2. 如果启用了池,并且存在的RealConnection个对象的总数大于最大大小,则抛出异常。
  3. 否则创建一个新的IDbConnection。初始化它,它将涉及打开某种网络连接(例如TCP / IP)或文件句柄(用于Access之类的东西),通过数据库的协议进行握手(因数据库类型而异)并授权连接。然后,这将变为RealConnection的{​​{1}}。
  4. RealConnection上执行的操作将转变为RealConnection在其网络连接(或其他)上执行的操作。结果转化为实现IDbConnection等的对象,以便为您的编程提供一致的界面。

    如果使用IDbConnection创建了RealConnection,那么该数据加载器将获得IDataReader的“所有权”。

    当您致电IDataReader时,会发生下列情况之一:

    1. 如果池化,并且池未满,则将该对象放入队列以供以后操作使用。
    2. 否则CommandBehavior.CloseConnection将执行任何协议定义的过程以结束连接(向连接将要关闭的数据库发送信号)并关闭网络连接等。然后,对象可以脱离范围并可用于垃圾收集。
    3. 例外情况是RealConnection案例发生,在Close()上调用RealConnectionCommandBehavior.CloseConnection会触发此事件。

      如果您致电Close(),那么根据Dispose()发生同样的事情。不同之处在于IDataReader被视为“清理”并且可以与Dispose()一起使用,而Close()可能会在生命中期使用,之后是Dispose() }}

      由于using对象的使用以及它们被合并的事实,打开和关闭连接从相对较重变为相对较轻。因此,长时间保持连接打开以避免打开它们的开销是很重要的,因此尽可能短的时间保持它们的开放变得很重要,因为Close()为您处理开销,使用它们的速度越快,合并连接在使用之间共享的效率就越高。

      另请注意,Open() RealConnection您已拨打RealConnection就可以了(规则是Dispose()应始终安全的规则,无论什么状态,实际上即使已经被召唤了)。因此,如果您手动调用IDbConnection,那么在Close()块中建立连接仍然是好的,以便在调用Dispose()之前捕获异常发生的情况。唯一的例外是你真正希望连接保持开放的地方;假设您返回使用Close()创建的using,在这种情况下,您不会释放Close(),但执行会处置读者。

      如果您未能处理连接,则IDataReader将不会返回到池中以供重用,或者执行其关闭过程。池将达到其限制,或者底层连接的数量将增加到破坏性能并阻止更多创建。最终CommandBehavior.CloseConnection上的终结者可以被调用并导致这个被修复,但最终确定只会减少伤害并且不能依赖。 (IDbConnection不需要终结器,因为它是保存非托管资源和/或需要关闭的RealConnection

      除此之外,假设除了RealConnection之外还有其他一些处理要求是独一无二的,即使分析上述情况导致您认为没有必要,也应该将其处理掉(例外情况是当IDbConnection将所有处置负担传递给RealConnection时,但处置该读者同样重要。

答案 1 :(得分:4)

好问题。

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

引擎盖下的步骤

  1. 打开物理套接字/管道(使用给定的驱动程序,例如ODBC)
  2. 与SQL Server握手
  3. 协商的连接字符串/凭据
  4. 交易范围
  5. 更不用说连接池,我相信会涉及某种算法(如果连接字符串与已存在的池匹配,则连接被添加到池中,否则会创建新的连接)

    <强> IDiposable

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

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

    这些要点,至少是实施IDisposable的好习惯。

答案 2 :(得分:0)

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