代码分析工具

时间:2014-07-02 13:20:04

标签: c# dispose sqlconnection

我正在使用Visual Studio的代码分析工具,它给我的一个警告是“不要多次处置对象:对象'conn'可以在方法'CycleMessages.discernScan_Reprint()'中多次处理。避免生成System.OjectDisposedEx你不应该在一个对象上多次调用Dispose。行:61

    private void discernScan_Reprint()
    {
        try
        {
            DataTable dt = new DataTable();

            SqlConnection conn = new SqlConnection("my constring");
            using (conn)
            {
                SqlCommand cmd = new SqlCommand("usp_myproc", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                using(cmd)
                {
                    cmd.Parameters.Add(new SqlParameter("@param", param));
                    conn.Open();
                    SqlDataReader dr = cmd.ExecuteReader();
                    dt.Load(dr);
                    conn.Close();  // this is line 61
                }
            }

            switch(dt.Rows.Count)
            {
                default:
                    throw new Exception("message");
                case 1:
                    //do Stuff
                case 0:
                    //do stuff
                    break;
            }

        }
        catch (Exception ex) {throw;}
    }

我没有处理conn(显式地通过conn.Dispose();),我只是关闭它并允许使用封装来conn对象 - 我知道我可以允许它通过处理关闭它,但为什么说我要两次处理呢?如果有的话它应该警告我说“你不需要终止对将被处置的物体的连接”或类似的东西。我错过了什么吗?

编辑: 来自关闭的参考链接...

  

Close方法回滚所有待处理的事务。然后,它会释放与连接池的连接,或者在禁用连接池时关闭连接。

  

如果SqlConnection超出范围,则不会关闭。因此,您必须通过调用Close或Dispose显式关闭连接。关闭和处置在功能上是等效的。如果连接池值Pooling设置为true或yes,则将基础连接返回到连接池。另一方面,如果Pooling设置为false或no,则关闭与服务器的基础连接。

我知道功能性Close()与dispose相同,但这不是我理解的字面意思。当我关闭物体时它没有被丢弃。它被关闭或返回到连接池,处理(再次从我的理解)内部调用close()方法 - 所以在冗余的时候,我仍然感到困惑,为什么它明确地说它已经被处理掉了,当它不是时。

4 个答案:

答案 0 :(得分:5)

Dispose pattern表明实现者为Dispose提供了在对象上下文中有意义的同义词。 SqlConnection上的一个同义词是Close()

  

关闭和处理在功能上是等效的。

由于您明确调用Close(),并且在连接的Dispose()语句结束时正在调用对象的using方法,因此您实际上有两次调用Dispose()

最好的方法是让using块为您处理它,因为它保证即使在Dispose()块内发生异常时也会调用using。它还将变量设置为null,以便尽快进行GC。


编辑回复@ alykin的问题

文档说Close()Dispose()方法在功能上是等价的,但@alykin已经确定了一个他们实际上并没有做同样事情的场景。如果我正确地阅读她的评论,它的工作原理如下:

以下作品:

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 

// ...

conn.Open();

cmd.ExecuteSomething();

cmd.Close();

// ... time passes ...

conn.Open();

以下内容不是:

SqlConnection conn = GetConnSomehow();
SqlCommand cmd = conn.CreateCommand(); 

using ( conn ) {
    cmd.ExecuteSomething();
}

// ... time passes ...

// This won't compile, according to alykins.
conn.Open();

这表明SqlConnection对象可以重用,至少在它们只有Close()时才会被重用。

可能使用using块的第二个示例无法编译的原因是编译器知道conn块结束时using已设置为null,因此它知道您可以不要在空对象引用上调用方法。

我仍然不确定这表明Dispose()实际上与Close()不同,因为由于使用块的语义归零而产生不协调。值得测试一下SqlConnection是否可以在Dispose()'之后重新打开但不能被清空。即使是这样,我也不会依赖于这种行为,因为它违反了微软在Dispose Pattern documentation中设置的指南。

此外,我不会使用不使用using块的第一个块 - 如果发生异常,连接可能会泄漏,或者至少在非确定性的时间内保持打开状态,直到GC看到该对象已被泄露并调用其终结器。

我不会依赖Close()Dispose()之间行为的任何差异 - 我建议不要尝试重新打开以前关闭的SqlConnection对象。即使你关闭或处理你交给的SqlConnection对象,让pooler句柄实际保持连接处于活动状态。


关于使用陈述的说明。

考虑以下代码块:

IDisposable thing = GetThing();

using ( thing ) {
   thing.DoWork();
}

该代码块完全与此块完全相同:

IDisposable thing = GetThing();

try {
   thing.DoWork();
}
finally {
   thing.Dispose();
   thing = null;
}

以下块,正如Microsoft所考虑的那样,他们的文档和分析工具被视为两个部分:

SqlConnection conn = GetConn();

using ( conn ) {
    DoWork(conn);
    conn.Close(); // code analysis tools count this as one Dispose().
} // implicit conn.Dispose() from the using block, so that's two.

忽略Close和Dispose不完全做同样事实。他们不希望你依赖它,也不应该在行为确实得到修复的情况下。

答案 1 :(得分:0)

它通知您显式关闭是提前处置资源。 using语句将自动为您处理它。

答案 2 :(得分:0)

close();dispose();基本上做同样的事情,这就是你收到此警告的原因。有关详细信息,请查看此link

答案 3 :(得分:0)

根据MSDN

  

关闭和处置功能相同。

因此,调用.Close()处理对象。此外,由于对象位于using块中,因此编译器也会调用.Dispose()。只需要两个中的一个。 (在这种情况下推荐使用后者。)

基本上,您只需要删除对Close()的调用,因为using块在处理对象时会为您处理:

using (conn)
{
    SqlCommand cmd = new SqlCommand("usp_myproc", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    using(cmd)
    {
        cmd.Parameters.Add(new SqlParameter("@param", param));
        conn.Open();
        System.Data.SqlClient.SqlDataReader dr = cmd.ExecuteReader();
        dt.Load(dr);
    }
}
相关问题