2012-02-16 89 views
14

我正在查看一些代码并与同事讨论。使用声明connection.open

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

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

的问题上来:

“为何不动的cn.Open到getConnection方法。”

我说如果“打开”引发异常处置不会被调用。他的回应是:

“那么,什么。连接没有打开,那么为什么它需要得到 关闭(或处置)?”

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

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

对我来说,如果存在其中调用cn.Open,它抛出一个场景,异常的处置将是不明确需要被调用。

在我下面的例子

那么,真的有什么区别“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 
     } 
    } 
+0

所以根据你在说什么..我在看什么Dispose将在WhyNotDoItTHisWay方法中被调用的唯一方法..仅仅因为你调用Open不会自动处理连接这是否有意义。 。将var cn包装在using(){}中,cn会自动假设你已经新建了那个实例。 – MethodMan 2012-02-16 18:41:40

+0

如果你使用** using **语句,你不应该处理你自己的对象。正如你的同事怀疑如果尝试打开连接时发生异常,该对象不包含实际处理的信息。此外,正如Servy所建议的,如果你真的想要安全,你可以简单地使用try()catch()finally()块。 – 2012-02-16 18:47:35

回答

7

您在编写代码的方式,你总是尽快打开连接,因为它是间没有什么区别。

但是,您可以多次打开和关闭连接,并在代码中设计这样做有很大的不同。

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

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

+0

你也可以提供这两种方法。一个“GetOpenConnection”和一个“GetUnopenedConnection”,这样你就可以做,但不需要为最常见的情况输入太多。 – Servy 2012-02-16 18:43:13

+1

你可以,但恕我直言,这不是一个好的API设计。 API应该是最小的,提供像这样的复合方法只是为了减少打字感觉像为我设计一个API的丑陋方式。但是如果你想要一个像这样的连接对象,你可以写一个包装器。 – 2012-02-16 18:48:30

+0

同意。如果它不仅仅是一小段代码,它可能是值得讨论的,但它很容易混淆API。鉴于OP首先开始了这个职位,但显​​然这对某些人有一定的价值,所以我反正提到了它。 – Servy 2012-02-16 18:54:22

1

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

+0

在返回之前不会处理连接...那么重要的是什么? – 2012-02-16 21:37:21

+0

@jsobo你是对的。应该尝试/赶上,而不是最后尝试。编辑。 – Servy 2012-02-16 21:39:07

6

我会倾向于返回SqlConnection的实例,而无需在您的方法中调用Open()。如果需要,应该这样做。在您的实用功能中不需要它。

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

回过头几步,应该是调用代码的责任来处理与SqlConnection完全相同的操作。

+0

这是最安全的选择,因为它是什么时候以及如果你需要打电话处置模糊,这将至少确保它总是被称为(禁止有人拉电源线等......) – 2012-02-16 21:43:08

+0

我重读了整个MSDN文章和发现这个......“我们强烈建议你在完成使用后始终关闭连接,以便将连接返回到池中。可以使用Connection对象的Close或Dispose方法或通过打开C#中使用语句中的所有连接“......又一个模棱两可的语句。它没有指定你实际上在连接上调用open!只是你用过它。 – 2012-02-20 12:46:58

0

在对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方法中打开连接。

当然这里发布的所有其他论据仍然有效;你应该只打开连接,如果你要立即使用它。

+0

这并不一定证明什么......如果连接打开有时工作但失败其他...会导致连接池被创建,然后资源检索,然后不处理? IE ...打开没有真正开放,但在封面试图做一个连接重置? – 2012-02-16 19:53:08

+0

@jsobo好的,你会如何测试?我尝试了工作和非工作连接字符串以及不同类型的错误的不同组合。据我所知,只要'SqlConnection.Open'引发异常,它就会清除所有底层资源。 – 2012-02-16 21:29:15

+0

我不知道......有很多场景你都无法测试。这个问题的确定是由于你在NEW或OPEN时得到了必须处置的东西。如果你新...并从池中获取某些东西,然后打开连接重置到现有的连接,它会抛出......然后你最好调用处置...如果它从池中检索并重置在封面(仅限打开),然后抛出,你从来没有从游泳池的连接,并不会处置... – 2012-02-16 21:40:10