【问题标题】:in a "using" block is a SqlConnection closed on return or exception?在“使用”块中是 SqlConnection 在返回或异常时关闭?
【发布时间】:2011-01-17 20:55:34
【问题描述】:

第一个问题:
说我有

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}

连接是否关闭?因为从技术上讲,我们永远不会像之前的return 那样到达最后一个}

第二个问题:
这次我有:

try
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        int employeeID = findEmployeeID();

        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();
    }
}
catch (Exception) { /*Handle error*/ }

现在,在try 的某个地方说,我们收到一个错误并被捕获。连接是否仍然关闭?因为同样,我们跳过try 中的其余代码,直接转到catch 语句。

我对@9​​87654328@ 的工作方式的思考是否过于线性?即当我们离开using 范围时,Dispose() 是否会被调用?

【问题讨论】:

    标签: c# using sqlconnection


    【解决方案1】:
    1. 是的
    2. 是的。

    无论哪种方式,当 using 块退出(成功完成或错误)时,它都会关闭。

    虽然我认为像这样组织会更好,因为它更容易看到将要发生的事情,即使对于将支持的新维护程序员稍后:

    using (SqlConnection connection = new SqlConnection(connectionString)) 
    {    
        int employeeID = findEmployeeID();    
        try    
        {
            connection.Open();
            SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
            command.CommandTimeout = 5;
    
            command.ExecuteNonQuery();    
        } 
        catch (Exception) 
        { 
            /*Handle error*/ 
        }
    }
    

    【讨论】:

    • 问题:使用Using语句时还需要OPEN连接吗?
    • 另外,如果您使用事务,通过在using 中使用try catch,您可以在catch 中显式地使用.Commit.Rollback 事务。这更具可读性和明确性,并且允许您在考虑到异常类型的情况下进行提交。 (如果未提交,事务会在 conn.Close 上隐式回滚。)。
    • @Fernando68 是的,您仍然需要Open 连接。 using 只保证对象的Dispose 方法被调用。
    • 我在 using 块中有返回 ExecuteScalar。当我第二次运行该方法时,它非常快,就像连接打开一样。为什么第二次这么快?
    • @positiveperspective - 查找 Connection Pooling
    【解决方案2】:

    两个问题都是。 using 语句被编译成 try/finally 块

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
    }
    

    一样
    SqlConnection connection = null;
    try
    {
        connection = new SqlConnection(connectionString);
    }
    finally
    {
       if(connection != null)
            ((IDisposable)connection).Dispose();
    }
    

    编辑:将演员固定为一次性 http://msdn.microsoft.com/en-us/library/yh598w02.aspx

    【讨论】:

    • 不完全是这样,但已经足够接近了。确切的区别并不重要。
    • @Bryan 没听懂,能否请您说出确切的区别,可以帮助我们更精益 :-)
    • 哇,这是很久以前发表的评论 :) 看起来好像在我发表评论的第二天进行了编辑。我想这就是我想的不同。
    • @Bryan 是的,我在您发表评论后进行了调整。
    • 我们是不是每次做其他操作时总是使用SqlConnection connection = new SqlConnection(connectionString)?说CRUD操作?每次我们插入数据时,我们都会执行 SqlConnection connection = new SqlConnection(connectionString)?
    【解决方案3】:

    这是我的模板。从 SQL 服务器中选择数据所需的一切。连接被关闭和释放,连接和执行中的错误被捕获。

    string connString = System.Configuration.ConfigurationManager.ConnectionStrings["CompanyServer"].ConnectionString;
    string selectStatement = @"
        SELECT TOP 1 Person
        FROM CorporateOffice
        WHERE HeadUpAss = 1 AND Title LIKE 'C-Level%'
        ORDER BY IntelligenceQuotient DESC
    ";
    using (SqlConnection conn = new SqlConnection(connString))
    {
        using (SqlCommand comm = new SqlCommand(selectStatement, conn))
        {
            try
            {
                conn.Open();
                using (SqlDataReader dr = comm.ExecuteReader())
                {
                    if (dr.HasRows)
                    {
                        while (dr.Read())
                        {
                            Console.WriteLine(dr["Person"].ToString());
                        }
                    }
                    else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
                }
            }
            catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
            if (conn.State == System.Data.ConnectionState.Open) conn.Close();
        }
    }
    

    * 修订日期:2015-11-09 *
    正如 NickG 所建议的那样;如果大括号太多让你厌烦,格式如下...

    using (SqlConnection conn = new SqlConnection(connString))
       using (SqlCommand comm = new SqlCommand(selectStatement, conn))
       {
          try
          {
             conn.Open();
             using (SqlDataReader dr = comm.ExecuteReader())
                if (dr.HasRows)
                   while (dr.Read()) Console.WriteLine(dr["Person"].ToString());
                else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
          }
          catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
          if (conn.State == System.Data.ConnectionState.Open) conn.Close();
       }
    

    再一次,如果您为 EA 或 DayBreak 游戏工作,您也可以放弃任何换行符,因为这些换行符只适用于稍后必须回来查看您的代码并且真正关心您的人?我对吗?我的意思是 1 行而不是 23 行意味着我是一个更好的程序员,对吧?

    using (SqlConnection conn = new SqlConnection(connString)) using (SqlCommand comm = new SqlCommand(selectStatement, conn)) { try { conn.Open(); using (SqlDataReader dr = comm.ExecuteReader()) if (dr.HasRows) while (dr.Read()) Console.WriteLine(dr["Person"].ToString()); else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)"); } catch (Exception e) { Console.WriteLine("Error: " + e.Message); } if (conn.State == System.Data.ConnectionState.Open) conn.Close(); }
    

    呼……好的。我把它从我的系统中取出来,并在一段时间内自娱自乐。继续。

    【讨论】:

    • 你知道你可以堆叠 using 语句而不需要额外的大括号吗?删除最后一个大括号,然后将 using 语句并排放置 :)
    • 是的,先生。谢谢你。我知道,但希望我的代码能够准确显示正在发生的事情,而无需使用太多其他捷径。不过,要添加到最终读者的好笔记。
    • 为什么最后用conn.Close();using 声明不是通过处置为您做到这一点吗?
    • 我相信现在可以(从 .net 3.5 开始)。在 .net 2.0 早期我并不清楚,所以我只是养成了检查和关闭的习惯。
    • c# 现在支持多个参数,因此您可以删除嵌套和双重 using 语句。
    【解决方案4】:

    Dispose 只是在您离开使用范围时被调用。 “使用”的目的是为开发人员提供一种有保证的方式来确保资源得到处置。

    来自MSDN

    using 语句可以在到达 using 语句的末尾时退出,或者如果抛出异常并且控制在语句结束之前离开语句块。

    【讨论】:

      【解决方案5】:

      Using 围绕正在分配的对象生成 try / finally 并为您调用 Dispose()

      它为您省去了手动创建 try / finally 块和调用 Dispose() 的麻烦

      【讨论】:

        【解决方案6】:

        在您的第一个示例中,C# 编译器实际上会将 using 语句转换为以下内容:

        SqlConnection connection = new SqlConnection(connectionString));
        
        try
        {
            connection.Open();
        
            string storedProc = "GetData";
            SqlCommand command = new SqlCommand(storedProc, connection);
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        
            return (byte[])command.ExecuteScalar();
        }
        finally
        {
            connection.Dispose();
        }
        

        Finally 语句总是在函数返回之前被调用,因此连接总是被关闭/释放。

        因此,在您的第二个示例中,代码将编译为以下内容:

        try
        {
            try
            {
                connection.Open();
        
                string storedProc = "GetData";
                SqlCommand command = new SqlCommand(storedProc, connection);
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        
                return (byte[])command.ExecuteScalar();
            }
            finally
            {
                connection.Dispose();
            }
        }
        catch (Exception)
        {
        }
        

        finally 语句中会捕获异常并关闭连接。外部 catch 子句不会看到异常。

        【讨论】:

        • 很好的例子,但是我不同意你的最后评论,如果在 using 块中发生异常,它将在任何外部捕获中被捕获而没有问题,实际上我通过编写 2 对其进行了测试在 try/catch 块中使用块,令我惊讶的是,我收到了来自内部第二个 using 块的异常错误消息。
        【解决方案7】:

        我在 try/catch 块内编写了两个 using 语句,我可以看到如果放置异常,则以相同的方式捕获异常在内部 using 语句中,就像 ShaneLS example

             try
             {
               using (var con = new SqlConnection(@"Data Source=..."))
               {
                 var cad = "INSERT INTO table VALUES (@r1,@r2,@r3)";
        
                 using (var insertCommand = new SqlCommand(cad, con))
                 {
                   insertCommand.Parameters.AddWithValue("@r1", atxt);
                   insertCommand.Parameters.AddWithValue("@r2", btxt);
                   insertCommand.Parameters.AddWithValue("@r3", ctxt);
                   con.Open();
                   insertCommand.ExecuteNonQuery();
                 }
               }
             }
             catch (Exception ex)
             {
               MessageBox.Show("Error: " + ex.Message, "UsingTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
             }
        

        无论 try/catch 放在哪里,都会毫无问题地捕获异常。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-15
          • 2013-06-02
          • 2010-09-15
          • 2018-01-28
          • 1970-01-01
          • 2016-03-15
          相关资源
          最近更新 更多