ในบล็อก“ using” เป็น SqlConnection ปิดคืนหรือยกเว้น?


136

คำถามแรก:
บอกว่าฉันมี

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คำสั่งโดยตรง

ฉันกำลังคิดเชิงเส้นเกินไปในการusingทำงานอย่างไร ie Dispose()ได้รับการเรียกเมื่อเราออกจากusingขอบเขตหรือไม่?

คำตอบ:


178
  1. ใช่
  2. ใช่.

ไม่ว่าจะด้วยวิธีใดเมื่อบล็อกการใช้งานถูกออก (ไม่ว่าจะสำเร็จโดยสมบูรณ์หรือโดยข้อผิดพลาด) บล็อกจะถูกปิด

แม้ว่าฉันคิดว่ามันจะเป็นการดีกว่าที่จะจัดระเบียบเช่นนี้เพราะมันง่ายกว่าที่จะเห็นสิ่งที่จะเกิดขึ้นแม้สำหรับโปรแกรมเมอร์บำรุงรักษาใหม่ที่จะสนับสนุนในภายหลัง:

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*/ 
    }
}

3
@TrueWill - ฉันเห็นด้วย ฉันเพิ่งย้ายโค้ดไปรอบ ๆ เพื่อหาโครงสร้าง
David

10
คำถาม: ฉันจำเป็นต้องเปิดการเชื่อมต่อเมื่อใช้คำสั่ง Using หรือไม่?
Fandango68

3
นอกจากนี้ถ้าคุณกำลังใช้การทำธุรกรรมโดยมีtry catchภายในusingที่คุณอย่างชัดเจนสามารถ.Commitหรือการทำธุรกรรมใน.Rollback catchนี่เป็นทั้งการอ่านและชัดเจนมากขึ้นและอนุญาตให้คุณกระทำถ้ามันทำให้รู้สึกถึงประเภทของข้อยกเว้น (การทำธุรกรรมโดยconn.Closeอ้อมย้อนกลับหากไม่ได้กระทำ)
Chris

8
@ Fernando68 ใช่คุณยังต้องOpenเชื่อมต่อ usingรับประกันเพียงว่าDisposeวิธีการของวัตถุที่เรียกว่า
juharr

ฉันมี ExecuteScalar ที่กลับมาอยู่ข้างในโดยใช้บล็อค และเมื่อฉันรันเมธอดครั้งที่สองมันก็เร็วมากเช่นการเชื่อมต่อเปิดอยู่ ทำไมเป็นครั้งที่สองอย่างรวดเร็ว?
มุมมองในเชิงบวก

46

ใช่ทั้งสองคำถาม คำสั่งการใช้งานได้รับการรวบรวมเป็นลอง / ปิดกั้นในที่สุด

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 ไม่เข้าใจคุณช่วยกรุณาพูดถึงความแตกต่างที่แน่นอนสามารถช่วยเราเรียนรู้เพิ่มเติมได้ที่ :-)
mohits00691

ว้าวนั่นเป็นความเห็นที่ทำมานานแล้ว :) ดูเหมือนว่าจะมีการแก้ไขวันหลังจากที่ฉันได้แสดงความคิดเห็นว่า ฉันคิดว่านั่นคือความแตกต่างที่ฉันคิด
ไบรอัน

@ ไบรอันใช่ฉันได้ทำการแก้ไขหลังจากความคิดเห็นของคุณ
Ryan Pedersen

17

นี่คือแม่แบบของฉัน ทุกสิ่งที่คุณต้องการเลือกข้อมูลจากเซิร์ฟเวอร์ 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(); }

ว้าว ... ตกลง ฉันออกไปจากระบบของฉันและทำตัวเองให้สนุกอยู่พักหนึ่ง ดำเนินการต่อไป


6
คุณรู้หรือไม่ว่าคุณสามารถสแต็กโดยใช้คำสั่งโดยไม่ต้องใส่เครื่องหมายเพิ่มเติม? ลบวงเล็บปีกกาสุดท้ายจากนั้นวางคำสั่งการใช้ที่อยู่ติดกัน :)
NickG

ครับท่าน. ขอบคุณ. ฉันรู้ แต่ต้องการรหัสของฉันเพื่อแสดงสิ่งที่เกิดขึ้นโดยไม่ใช้ทางลัดอื่น ๆ มากเกินไป ดีที่ควรทราบในการเพิ่มผู้อ่านปลายทาง
ShaneLS

ทำไมคุณถึงใช้conn.Close();ในตอนท้าย? usingคำสั่งไม่ได้ทำเพื่อคุณผ่านการกำจัดเหรอ?
Fredrick Gauss

ฉันเชื่อว่ามันทำตอนนี้ (ตั้งแต่. net 3.5) มันไม่ชัดเจนสำหรับฉันในช่วงต้นด้วย. NET 2.0 ดังนั้นฉันจึงทำให้มันเป็นนิสัยในการตรวจสอบและปิด
ShaneLS

1
"หมายถึง 1 บรรทัดแทน 23 หมายความว่าฉันเป็นโปรแกรมเมอร์ที่ดีกว่าใช่มั้ย" ฉันชอบคุณ :-D
Philipp Müller

5

การกำจัดจะได้รับการเรียกเมื่อคุณออกจากขอบเขตการใช้งาน ความตั้งใจในการ "ใช้งาน" คือการให้วิธีที่รับประกันแก่นักพัฒนาเพื่อให้แน่ใจว่าทรัพยากรจะถูกกำจัด

จากMSDN :

การใช้คำสั่งสามารถออกได้ทั้งเมื่อถึงจุดสิ้นสุดของการใช้คำสั่งหรือถ้ามีข้อยกเว้นจะถูกโยนและการควบคุมออกจากบล็อกคำสั่งก่อนที่จะสิ้นสุดของคำสั่ง


5

Usingสร้างลอง / ในที่สุดรอบวัตถุที่มีการจัดสรรและเรียกร้องDispose()ให้คุณ

มันช่วยให้คุณไม่ต้องวุ่นวายกับการสร้างลอง / สุดท้ายบล็อกและการโทรด้วยตนเอง Dispose()


3

ในตัวอย่างแรกของคุณคอมไพเลอร์ 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();
}

คำสั่งสุดท้ายจะถูกเรียกเสมอก่อนที่ฟังก์ชันจะส่งคืนดังนั้นการเชื่อมต่อจะถูกปิด / กำจัดเสมอ

ดังนั้นในตัวอย่างที่สองของคุณรหัสจะถูกคอมไพล์ด้วยต่อไปนี้:

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)
{
}

ข้อยกเว้นจะถูกจับในคำสั่งในที่สุดและการเชื่อมต่อปิด ข้อยกเว้นจะไม่สามารถมองเห็นได้โดยประโยคจับด้านนอก


1
ผู้ชายตัวอย่างที่ดีมาก แต่ฉันไม่เห็นด้วยกับความคิดเห็นล่าสุดของคุณหากมีข้อยกเว้นเกิดขึ้นในบล็อกที่ใช้มันจะถูกจับโดยไม่มีปัญหาในการจับนอกใด ๆ ในความเป็นจริงฉันทดสอบโดยการเขียน 2 โดยใช้บล็อกภายในบล็อกลอง และด้วยความประหลาดใจของฉันฉันได้รับข้อความแสดงข้อผิดพลาดข้อยกเว้นแสดงว่ามาจากวินาทีที่ภายในโดยใช้บล็อก
WhySoSerious

1

ฉันเขียนสองใช้งบภายในลอง / จับบล็อกและฉันจะได้เห็นข้อยกเว้นที่ถูกจับในลักษณะเดียวกันถ้ามันวางอยู่ภายในชั้นในโดยใช้คำสั่งเช่นเดียวกับ ShaneLS ตัวอย่างเช่น

     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);
     }

ไม่ว่าจะลอง / จับที่ไหนข้อยกเว้นจะถูกดักจับโดยไม่มีปัญหา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.