【问题标题】:using statement with connection.open使用带有 connection.open 的语句
【发布时间】:2012-02-17 05:38:44
【问题描述】:

我正在查看一些代码并与同事讨论。

特别是一段看起来像这样的代码。

    [Test]
    public void TestNormalWay()
    {
        using(var cn = GetConnection())
        {
            cn.Open();
            // do stuff
        }
    }

问题来了:

“为什么不将cn.Open移到GetConnection方法中。”

我说过如果“Open”抛出异常 dispose 将不会被调用。他的回答是

“那又怎样。连接没有打开,为什么需要获取 关闭(或处置)?”

对我来说,这只是不想知道我是否需要处置/关闭,所以我会在代码中重复 cn.Open,而不是将其移动到共享函数中。

但是这很有趣......所以我在SQL Server Connection Pooling (ADO.NET)做了一些阅读

对我来说,不清楚是否存在调用 cn.Open 并抛出异常以及需要调用 dispose 的情况。

所以在我下面的例子中,“TestNormalWay”和“WhyNotDoItThisWay”之间真的有什么区别

    protected static DbConnection GetConnection()
    {
        DbConnection cn = new SqlConnection("SomeConnecitonstring... ");
        return cn; 
    }

    protected static DbConnection GetConnectionDangerousVersion()
    {
        DbConnection cn = new SqlConnection("SomeConnecitonstring... ");
        cn.Open();  // this will throw.. .dispose not called
        return cn; 
    }

    [Test]
    public void TestNormalWay()
    {
        using(var cn = GetConnection())
        {
            cn.Open();
            // do stuff
        }
    }

    [Test]
    public void WhyNotDoItThisWay()
    {
        using(var cn = GetConnectionDangerousVersion())
        {
            // do stuff
        }
    }

【问题讨论】:

  • 所以根据你所说的......以及我正在寻找的在WhyNotDoItTHisWay方法中调用Dispose的唯一方法..仅仅因为你调用Open不会自动释放连接这有意义吗.. 将 var cn 包裹在 using(){} 周围,cn 会自动处理,假设您也新建了该实例..
  • 如果您使用的是 using 语句,则不必自己处置该对象。正如您的同事怀疑在尝试打开连接时是否发生异常,该对象不包含要实际处理的信息。此外,根据 Servy 的建议,如果您真的想要安全,您可以简单地使用 try()catch()finally() 块。

标签: c# ado.net


【解决方案1】:

您编写代码的方式总是希望在连接创建后立即打开,因此没有区别。

但是,您可以多次打开和关闭连接,并且在设计用于执行此操作的代码中存在很大差异。

我可能想编写一些代码,其中我有一个长时间运行的例程,该例程需要一个连接对象并随着时间的推移打开和关闭它。该例程可能不关心连接对象是如何创建的。因此,将创建连接的行为与打开和关闭连接的行为分开是一个优势。

关于资源管理问题,我同意这不是问题。创建 SQL 连接对象本身不会锁定任何资源,打开它的行为会获取池连接。如果 open 返回异常,我认为假设连接未打开是合理的。

【讨论】:

  • 您也可以同时提供这两种方法。一个“GetOpenConnection”和一个“GetUnopenedConnection”,这样你就可以做到,但在最常见的情况下不需要输入太多。
  • 你可以,但恕我直言,这不是一个好的 API 设计。 API 应该是最小的,提供这样的复合方法只是为了减少打字对我来说是一种丑陋的设计 API 的方式。但是如果你想要一个这样的连接对象,你可以编写一个包装器。
  • 同意。如果它不仅仅是一行简短的代码,那么它可能值得讨论,但它很简单,值得把 API 弄得乱七八糟。鉴于 OP 首先开始发布该帖子,显然这对某人有一些价值,所以我还是提到了它。
  • @JamesGaunt MSDN 说“当请求 SqlConnection 对象时,如果可用连接可用,则从池中获取它。”如果我指定连接字符串,那是打开连接还是“新建”连接的行为。
  • 这篇文章令人困惑,因为在第三段中它说“每当用户在连接上调用 Open 时,池化程序都会在池中查找可用连接”,这与 jsobo found 的行相矛盾
【解决方案2】:

我倾向于只返回SqlConnection 的实例而不在您的方法中调用Open()。如果需要,就应该这样做。您的实用程序函数中不需要它。

其中一个原因是,有些对象需要SqlConnection,但不一定需要打开它们。例如,SqlDataAdapter 接受SqlConnection,并在其内部处理它的打开和关闭。当然,您可以在传递连接之前打开它,但是您必须明确关闭它。

退后几步,调用代码应该负责处理与SqlConnection 的具体关系。

【讨论】:

  • 这是最安全的选择,因为它对于何时以及是否需要调用 dispose 不明确,这至少可以确保它总是被调用(除非有人拉电源线等......)
  • 我重读了整篇msdn文章,发现这个..."我们强烈建议您在使用完毕后始终关闭连接,以便连接返回池中。您可以这样做这使用 Connection 对象的 Close 或 Dispose 方法,或者通过在 C# 中的 using 语句中打开所有连接“......再次是另一个模棱两可的语句。它没有指定您实际上在连接上调用了 open !只是你用过。
【解决方案3】:

您可以在第二个“危险”版本中的 .Open() 调用周围放置一个 try/catch,这样如果它在打开时抛出异常,您仍然可以处理连接。

【讨论】:

  • 在返回之前不会处理连接...那有什么意义呢?
  • @jsobo 你是对的。应该是 try/catch,而不是 try finally。已编辑。
【解决方案4】:

在 SqlConnection 的内部达到顶峰之后,我几乎确信这真的无关紧要。快速测试似乎证实了这一点:

static void Main( string[] args )
{
   Func<SqlConnection> getConnection =
            () =>
               {
                  var connection =
                     new SqlConnection(
                        "Initial Catalog=myDatabase;Server=(local);Username=bogus;password=blah;Connect Timeout=10;" );

                  connection.Open();
                  return connection;
               };

   while(true)
   {
      try
      {
         using( var connection = getConnection() )
         {
            var cmd = new SqlCommand( "SELECT 1", connection ) {CommandType = CommandType.Text};
            cmd.ExecuteNonQuery();
         }
      }
      catch ( Exception )
      {
         // ignore exception
      }
   }
}

我让这段代码运行了几分钟,并附加了一个分析器。它不仅运行速度非常快,而且还没有泄漏任何内存。这几乎让我相信在 GetConnection 方法中打开连接是可以的。

当然,这里发布的所有其他论点仍然有效;仅当您要立即使用时才应打开连接。

【讨论】:

  • 这不一定能证明什么...如果连接打开有时有效但其他连接失败...这会导致创建连接池然后检索资源然后不处理吗? IE ...打开实际上并没有打开,而是在试图重置连接?
  • @jsobo 好的,你会如何测试呢?我尝试了工作和非工作连接字符串的不同组合以及不同类型的错误。据我所知,每当SqlConnection.Open 抛出异常时,它都会清理所有底层资源。
  • 我不知道...有很多场景是您无法测试的。问题实际上取决于您何时得到必须处理的东西……在 NEW 或 OPEN 时。如果您是新的...并从池中获取某些内容,则 open 会将连接重置为现有连接并抛出...然后您最好调用 dispose... 如果它从池中检索并在幕后重置(仅.Open) 并抛出然后你永远不会从池中获得连接,也不必处理......
  • 我和詹姆斯·冈特一起做这个。查看源代码(带有反射器)并基于一些经验测试,我会说他是对的,并且创建连接不会占用任何资源。只有当您成功打开一个连接时,您才需要关闭它。
猜你喜欢
  • 2018-01-07
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2021-01-27
  • 2010-11-05
  • 2010-12-30
  • 2015-05-01
  • 2015-04-30
相关资源
最近更新 更多