จำเป็นต้องปิดและกำจัด SqlDataReader ด้วยตนเองหรือไม่?


90

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

สิ่งนี้อาจทำให้ประสิทธิภาพการทำงานช้าลงหรือไม่

คำตอบ:


125

พยายามหลีกเลี่ยงการใช้โปรแกรมอ่านแบบนี้:

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

ให้ห่อโดยใช้คำสั่ง:

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

คำแถลงการใช้จะช่วยให้มั่นใจได้ว่ามีการกำจัดวัตถุอย่างถูกต้องและการปลดปล่อยทรัพยากร

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


24
คุณไม่จำเป็นต้องมีคำสั่ง. Close () ในตัวอย่างใดตัวอย่างหนึ่ง: จัดการโดยการเรียก. Dispose ()
Joel Coehoorn

7
อาจต้องการตรวจสอบว่าเป็น. HasRows แทนที่จะเป็นโมฆะ
จอนห์

3
@Andrew ถ้า ExecuteReader แสดงข้อยกเว้นจะคืนค่า null ได้อย่างไร
csauve

7
@JohH: ในขณะที่ (reader.Read ()) ในตัวอย่างทำเช่นเดียวกับ. HasRows และคุณต้องอ่านอย่างไรก็ตามเพื่อเลื่อนผู้อ่านไปข้างหน้าไปยังแถวแรก
csauve

1
@csauve คุณพูดถูกฉันเดาว่ามันต้องไม่คืนค่าว่าง ฉันไม่แน่ใจว่าทำไมฉันถึงดูค่าของตัวแปร SqlDataReader
Andrew

54

โปรดทราบว่าการกำจัด SqlDataReader ที่สร้างอินสแตนซ์โดยใช้ SqlCommand.ExecuteReader () จะไม่ปิด / กำจัดการเชื่อมต่อพื้นฐาน

มีสองรูปแบบทั่วไป ในขั้นแรกผู้อ่านจะเปิดและปิดภายในขอบเขตของการเชื่อมต่อ:

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

บางครั้งการใช้วิธีการเข้าถึงข้อมูลเพื่อเปิดการเชื่อมต่อและส่งคืนเครื่องอ่านก็สะดวก ในกรณีนี้สิ่งสำคัญคือต้องเปิดเครื่องอ่านที่ส่งคืนโดยใช้ CommandBehavior.CloseConnection ดังนั้นการปิด / ทิ้งเครื่องอ่านจะเป็นการปิดการเชื่อมต่อที่สำคัญ รูปแบบมีลักษณะดังนี้:

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

และรหัสการโทรเพียงแค่ต้องการกำจัดผู้อ่านดังนั้น:

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

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

@alwayslearning นั่นคือสถานการณ์ที่ฉันมี ...... คุณสามารถปิด / กำจัด SqlCommand เมื่อคุณส่งคืน SqlDataReader ไปยังผู้โทรได้หรือไม่?
แกนเดอร์

1
นี้ไม่ดี. หากคุณทนไม่ได้จริงๆที่จะใช้usings ให้เรียกทิ้งในfinally {}บล็อกหลังจากจับได้ วิธีการเขียนคำสั่งที่ประสบความสำเร็จจะไม่ถูกปิดหรือกำจัด
smdrager

3
@smdrager ถ้าคุณอ่านคำตอบอย่างใกล้ชิดเขากำลังพูดถึงวิธีการที่ส่งคืนผู้อ่าน หากคุณใช้. ExecuteReader (CommandBehavior.CloseConnection); จากนั้นโดยการกำจัดเครื่องอ่านการเชื่อมต่อจะถูกปิด ดังนั้นวิธีการโทรจะต้องรวมตัวอ่านผลลัพธ์ไว้ในคำสั่งใช้เท่านั้น โดยใช้ (var rdr = SqlHelper.GetReader ()) {// ... } หากคุณปิดมันในบล็อกสุดท้ายผู้อ่านของคุณจะไม่สามารถอ่านได้เนื่องจากการเชื่อมต่อถูกปิด
Sinaesthetic

@ganders - กลับมาที่โพสต์เก่านี้: ใช่คุณทำได้และน่าจะทิ้ง SqlCommand - อัปเดตตัวอย่างเพื่อทำเช่นนั้น
โจ

11

เพื่อความปลอดภัยห่อทุก SqlDataReader วัตถุในการใช้คำสั่ง


พอใช้. อย่างไรก็ตามประสิทธิภาพจะสร้างความแตกต่างได้จริงหรือไม่หากไม่มีคำสั่งใช้?
Jon Ownbey

คำสั่งการใช้จะเหมือนกับการตัดโค้ด DataReader ในบล็อก try .. ในที่สุด ... ด้วยวิธีปิด / ทิ้งในส่วนสุดท้าย โดยพื้นฐานแล้วมันเพียงแค่ "รับประกัน" ว่าวัตถุจะถูกกำจัดอย่างถูกต้อง
ทอดด์

1
สิ่งนี้มาจากลิงค์ที่ฉันให้ไว้: "คำสั่งใช้ทำให้แน่ใจว่า Dispose ถูกเรียกแม้ว่าจะมีข้อยกเว้นเกิดขึ้นในขณะที่คุณเรียกใช้เมธอดบนวัตถุก็ตาม"
คอน

6
ต่อ ... "คุณสามารถบรรลุผลลัพธ์เดียวกันได้โดยการวางวัตถุไว้ในบล็อกลองแล้วเรียก Dispose ในบล็อกสุดท้ายอันที่จริงนี่คือวิธีที่คอมไพเลอร์แปลคำสั่งใช้"
คอน

5

เพียงห่อ SQLDataReader ของคุณด้วยคำสั่ง "ใช้" ที่ควรดูแลปัญหาส่วนใหญ่ของคุณ

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