Có cần thiết phải đóng và hủy bỏ SqlDataReader theo cách thủ công không?


90

Tôi đang làm việc với mã kế thừa ở đây và có nhiều trường hợp của mã SqlDataReaderđó không bao giờ bị đóng hoặc xử lý. Kết nối bị đóng nhưng tôi không chắc liệu có cần thiết phải quản lý trình đọc theo cách thủ công hay không.

Điều này có thể làm chậm hiệu suất không?

Câu trả lời:


124

Cố gắng tránh sử dụng các trình đọc như thế này:

SqlConnection connection = new SqlConnection("connection string");
SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);
SqlDataReader reader = cmd.ExecuteReader();
connection.Open();
if (reader != null)
{
      while (reader.Read())
      {
              //do something
      }
}
reader.Close(); // <- too easy to forget
reader.Dispose(); // <- too easy to forget
connection.Close(); // <- too easy to forget

Thay vào đó, hãy sử dụng các câu lệnh:

using(SqlConnection connection = new SqlConnection("connection string"))
{

    connection.Open();

    using(SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection))
    {
        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            if (reader != null)
            {
                while (reader.Read())
                {
                    //do something
                }
            }
        } // reader closed and disposed up here

    } // command disposed here

} //connection closed and disposed here

Câu lệnh using sẽ đảm bảo xử lý đúng đối tượng và giải phóng tài nguyên.

Nếu bạn quên thì bạn sẽ để việc dọn dẹp cho người thu gom rác, việc này có thể mất một lúc.


24
Bạn không cần câu lệnh .Close () trong cả hai mẫu: nó được xử lý bởi lệnh gọi .Dispose ().
Joel Coehoorn

7
Có lẽ muốn kiểm tra xem nó .HasRows thay vì null.
JonH

3
@Andrew Nếu ExecuteReader ném một ngoại lệ, làm cách nào nó có thể trả về null?
csauve

7
@JohH: while (reader.Read ()) trong ví dụ hoàn thành tương tự như .HasRows, và bạn cần phải .Read bằng mọi cách để đưa người đọc chuyển tiếp đến hàng đầu tiên.
csauve

1
@csauve Bạn nói đúng, tôi đoán nó không phải trả về null. Tôi không chắc tại sao tôi lại xem giá trị của biến SqlDataReader.
Andrew

53

Lưu ý rằng việc hủy bỏ một SqlDataReader được khởi tạo bằng SqlCommand.ExecuteReader () sẽ không đóng / hủy kết nối cơ bản.

Có hai mẫu phổ biến. Trong lần đầu tiên, đầu đọc được mở và đóng trong phạm vi kết nối:

using(SqlConnection connection = ...)
{
    connection.Open();
    ...
    using(SqlCommand command = ...)
    {
        using(SqlDataReader reader = command.ExecuteReader())
        {
            ... do your stuff ...
        } // reader is closed/disposed here
    } // command is closed/disposed here
} // connection is closed/disposed here

Đôi khi, thật tiện lợi khi có một phương thức truy cập dữ liệu mở kết nối và trả về một đầu đọc. Trong trường hợp này, điều quan trọng là trình đọc trả về phải được mở bằng CommandBehavior.CloseConnection, vì vậy việc đóng / hủy bỏ trình đọc sẽ đóng kết nối cơ bản. Mô hình trông giống như sau:

public SqlDataReader ExecuteReader(string commandText)
{
    SqlConnection connection = new SqlConnection(...);
    try
    {
        connection.Open();
        using(SqlCommand command = new SqlCommand(commandText, connection))
        {
            return command.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }
    catch
    {
        // Close connection before rethrowing
        connection.Close();
        throw;
    }
}

và mã gọi chỉ cần loại bỏ đầu đọc do đó:

using(SqlDataReader reader = ExecuteReader(...))
{
    ... do your stuff ...
} // reader and connection are closed here.

Trong đoạn mã thứ hai, nơi phương thức trả về một SqlDataReader, lệnh không được xử lý. Điều đó có ổn không và có thể hủy lệnh (đặt nó trong một khối đang sử dụng) và sau đó trả lại trình đọc không?
alwayslearning 21/10/11

@alwayslearning đó chính xác là kịch bản mà tôi gặp phải ...... bạn có thể đóng / hủy bỏ SqlCommand khi bạn đang trả lại SqlDataReader cho người gọi không?
ganders

1
Điều này tệ đây. Nếu bạn THỰC SỰ không thể sử dụng usings thì hãy gọi vứt bỏ trong finally {}khối sau khi bắt. Theo cách này được viết, các lệnh thành công sẽ không bao giờ bị đóng hoặc xử lý.
smdrager

2
@smdrager, nếu bạn đọc câu trả lời gần hơn, anh ấy đang nói về một phương pháp trả về một người đọc. Nếu bạn sử dụng .ExecuteReader (CommandBehavior.CloseConnection); sau đó bằng cách loại bỏ READER, kết nối sẽ bị đóng. Vì vậy, phương thức gọi chỉ cần bao bọc đầu đọc kết quả trong một câu lệnh using. using (var rdr = SqlHelper.GetReader ()) {// ...} nếu bạn đã đóng nó trong khối cuối cùng, thì trình đọc của bạn sẽ không đọc được vì kết nối bị đóng.
Sinaesthetic

@ganders - quay lại bài đăng cũ này: vâng bạn có thể và có lẽ nên loại bỏ SqlCommand - đã cập nhật ví dụ để làm như vậy.
Joe

11

Để an toàn, hãy bọc mọi đối tượng SqlDataReader trong một câu lệnh using .


Đủ công bằng. Tuy nhiên, nó có thực sự tạo ra sự khác biệt về hiệu suất nếu không có câu lệnh using?
Jon Ownbey

Một câu lệnh using cũng giống như gói mã DataReader trong một khối try..finally ..., với phương thức close / dispose trong phần cuối cùng. Về cơ bản, nó chỉ "đảm bảo" rằng đối tượng sẽ được xử lý đúng cách.
Todd

Đây là trực tiếp từ liên kết mà tôi đã cung cấp: "Câu lệnh using đảm bảo rằng Dispose được gọi ngay cả khi một ngoại lệ xảy ra trong khi bạn đang gọi các phương thức trên đối tượng."
Kon

5
Tiếp tục ... "Bạn có thể đạt được kết quả tương tự bằng cách đặt đối tượng bên trong khối try và sau đó gọi Dispose trong khối cuối cùng; trên thực tế, đây là cách trình biên dịch dịch câu lệnh using."
Kon

5

Chỉ cần bọc SQLDataReader của bạn bằng câu lệnh "using". Điều đó sẽ giải quyết hầu hết các vấn đề của bạ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.