trong một khối sử dụng khối khối là một SqlConnection bị đóng khi trả lại hoặc ngoại lệ?


136

Câu hỏi đầu tiên:
Nói tôi có

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

Kết nối có bị đóng không? Bởi vì về mặt kỹ thuật, chúng tôi không bao giờ đi đến người cuối cùng }như chúng tôi returntrước đó.

Câu hỏi thứ hai:
Lần này tôi có:

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

Bây giờ, nói ở đâu đó trong trychúng tôi nhận được một lỗi và nó bị bắt. Kết nối vẫn bị đóng? Bởi vì một lần nữa, chúng tôi bỏ qua phần còn lại của mã trong tryvà đi thẳng đến catchcâu lệnh.

Tôi có suy nghĩ quá tuyến tính trong cách làm usingviệc không? tức là Dispose()chỉ đơn giản được gọi khi chúng ta rời khỏi usingphạm vi?

Câu trả lời:


178
  1. Đúng
  2. Đúng.

Dù bằng cách nào, khi khối sử dụng được thoát (do hoàn thành thành công hoặc do lỗi), nó đã bị đóng.

Mặc dù tôi nghĩ sẽ tốt hơn nếu tổ chức như thế này vì sẽ dễ dàng hơn để xem điều gì sẽ xảy ra, ngay cả đối với lập trình viên bảo trì mới sẽ hỗ trợ sau:

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 - Tôi đồng ý. Tôi chỉ di chuyển mã xung quanh một chút cho cấu trúc.
David

10
Câu hỏi: Tôi có cần MỞ kết nối khi sử dụng câu lệnh Sử dụng không?
Fandango68

3
Ngoài ra nếu bạn đang sử dụng giao dịch, bằng cách có try catchbên trong usingbạn có thể rõ ràng .Commithoặc .Rollbackgiao dịch trong catch. Điều này vừa dễ đọc vừa rõ ràng hơn và cho phép bạn cam kết nếu điều đó hợp lý với loại ngoại lệ. (Giao dịch hoàn toàn quay trở lại conn.Closenếu không được cam kết.).
Chris

8
@ Fernando68 Vâng, bạn vẫn phải Openkết nối. usingchỉ đảm bảo rằng Disposephương thức của đối tượng được gọi.
juharr

Tôi có trả lại ExecuteScalar bên trong bằng cách sử dụng các khối. Và khi tôi chạy phương thức lần thứ hai thì nó rất nhanh, giống như kết nối đã mở. Tại sao lần thứ hai lại nhanh đến vậy?
quan điểm tích cực

46

Có cho cả hai câu hỏi. Câu lệnh sử dụng được biên dịch thành một khối thử / cuối cùng

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

giống như

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

Chỉnh sửa: Sửa lỗi diễn viên thành Dùng một lần http://msdn.microsoft.com/en-us/l Library / ry598w02.aspx


Nó không chính xác như vậy, nhưng nó đủ gần. sự khác biệt chính xác không quan trọng.
Bryan

@Bryan đã không nhận được nó, bạn có thể vui lòng đề cập đến sự khác biệt chính xác, có thể giúp chúng tôi dựa nhiều hơn :-)
mohits00691

Wow, đó là một bình luận được thực hiện từ lâu :) Có vẻ như có một chỉnh sửa vào ngày sau khi tôi đưa ra nhận xét đó. Tôi nghĩ đó là sự khác biệt mà tôi đã nghĩ đến.
Bryan

@Bryan Có, tôi đã sửa điều chỉnh sau bình luận của bạn.
Ryan Pedersen

17

Đây là Mẫu của tôi. Mọi thứ bạn cần để chọn dữ liệu từ máy chủ SQL. Kết nối được đóng và xử lý và các lỗi trong kết nối và thực thi được bắt gặp.

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

* Sửa đổi: 2015-11-09 *
Theo đề xuất của NickG; Nếu quá nhiều niềng răng làm phiền bạn, định dạng như thế này ...

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

Sau đó, một lần nữa, nếu bạn làm việc cho các trò chơi EA hoặc DayBreak, bạn cũng có thể từ bỏ bất kỳ ngắt dòng nào vì chúng chỉ dành cho những người phải quay lại và xem mã của bạn sau này và ai thực sự quan tâm? Tôi có đúng không Ý tôi là 1 dòng thay vì 23 nghĩa là tôi là một lập trình viên tốt hơn, phải không?

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

Phù ... OK. Tôi đã đưa nó ra khỏi hệ thống của mình và tôi đã rất vui khi được một lúc. Tiếp tục.


6
Bạn có biết bạn có thể xếp chồng bằng cách sử dụng các câu lệnh mà không cần thêm dấu ngoặc nhọn không? Xóa dấu ngoặc cuối cùng, sau đó đặt các câu lệnh sử dụng cạnh nhau :)
NickG

Vâng thưa ngài. Cảm ơn bạn. Tôi biết nhưng muốn mã của tôi hiển thị chính xác những gì đang xảy ra mà không sử dụng quá nhiều lần cắt ngắn khác. Lưu ý tốt để thêm vào đầu đọc mặc dù.
ShaneLS

Tại sao bạn sử dụng conn.Close();vào cuối? Không usingtuyên bố làm điều đó cho bạn thông qua việc xử lý?
Fredrick Gauss

Tôi tin rằng nó hiện tại (kể từ .net 3.5). Nó không rõ ràng với tôi từ sớm với .net 2.0 vì vậy tôi chỉ tạo thói quen kiểm tra và đóng.
ShaneLS

1
"có nghĩa là 1 dòng thay vì 23 có nghĩa là tôi là một lập trình viên tốt hơn, phải không?" Tôi thích bạn :-D
Philipp Müller

5

Vứt bỏ đơn giản được gọi khi bạn rời khỏi phạm vi sử dụng. Mục đích của việc "sử dụng" là cung cấp cho các nhà phát triển một cách bảo đảm để đảm bảo rằng các tài nguyên được xử lý.

Từ MSDN :

Một câu lệnh sử dụng có thể được thoát khi kết thúc câu lệnh sử dụng hoặc nếu một ngoại lệ được ném và điều khiển rời khỏi khối câu lệnh trước khi kết thúc câu lệnh.


5

Usingtạo một thử / cuối cùng xung quanh đối tượng được phân bổ và gọi Dispose()cho bạn.

Nó giúp bạn tiết kiệm rắc rối khi tự tạo khối thử / cuối cùng và gọi Dispose()


3

Trong ví dụ đầu tiên của bạn, trình biên dịch C # sẽ thực sự dịch câu lệnh bằng cách sử dụng như sau:

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

Cuối cùng, các câu lệnh sẽ luôn được gọi trước khi hàm trả về và do đó kết nối sẽ luôn được đóng / xử lý.

Vì vậy, trong ví dụ thứ hai của bạn, mã sẽ được biên dịch như sau:

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

Ngoại lệ sẽ được bắt gặp trong tuyên bố cuối cùng và kết nối đóng lại. Ngoại lệ sẽ không được nhìn thấy bởi mệnh đề bắt bên ngoài.


1
Người đàn ông ví dụ rất tốt, nhưng tôi không đồng ý với nhận xét cuối cùng của bạn, nếu một ngoại lệ xảy ra trong một khối sử dụng, nó sẽ bị bắt mà không gặp vấn đề gì với bất kỳ bắt bên ngoài nào, thực tế tôi đã kiểm tra nó bằng cách viết 2 khối bên trong khối thử / bắt và thật ngạc nhiên, tôi nhận được thông báo lỗi ngoại lệ xuất hiện từ khối thứ hai bên trong bằng cách sử dụng khối.
WhySoSerious

1

Tôi đã viết hai câu lệnh bằng cách sử dụng khối lệnh try / Catch và tôi có thể thấy ngoại lệ được bắt theo cùng một cách nếu nó được đặt trong câu lệnh sử dụng bên trong giống như ví dụ của 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);
     }

Bất kể nơi nào thử / bắt được đặt, ngoại lệ sẽ được bắt mà không có vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.