Apakah SqlDataReader harus ditutup dan dibuang secara manual?

90

Saya bekerja dengan kode lama di sini dan ada banyak contoh SqlDataReaderyang tidak pernah ditutup atau dibuang. Sambungan ditutup tetapi, saya tidak yakin apakah perlu mengelola pembaca secara manual.

Mungkinkah ini menyebabkan perlambatan kinerja?

Jon Ownbey
sumber

Jawaban:

125

Cobalah untuk menghindari penggunaan pembaca seperti ini:

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

Sebagai gantinya, bungkus dengan menggunakan pernyataan:

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

Pernyataan penggunaan akan memastikan pembuangan objek yang benar dan membebaskan sumber daya.

Jika Anda lupa, maka Anda menyerahkan pembersihan ke pengumpul sampah, yang bisa memakan waktu cukup lama.

Codebrain
sumber
24
Anda tidak memerlukan pernyataan .Close () di kedua sampel: itu ditangani oleh panggilan .Dispose ().
Joel Coehoorn
7
Mungkin ingin memeriksa apakah itu .HasRows daripada null.
JonH
3
@ Andrew Jika ExecuteReader melontarkan pengecualian, bagaimana cara mengembalikan null?
csauve
7
@JohH: while (reader.Read ()) dalam contoh mencapai hal yang sama seperti .HasRows, dan Anda perlu .Read untuk memajukan pembaca ke baris pertama.
csauve
1
@csauve Anda benar, saya rasa itu pasti tidak mengembalikan nol. Saya tidak yakin mengapa saya melihat nilai variabel SqlDataReader.
Andrew
54

Perhatikan bahwa membuang SqlDataReader yang dibuat menggunakan SqlCommand.ExecuteReader () tidak akan menutup / membuang koneksi yang mendasarinya.

Ada dua pola umum. Yang pertama, pembaca dibuka dan ditutup dalam ruang lingkup koneksi:

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

Terkadang nyaman untuk memiliki metode akses data membuka koneksi dan mengembalikan pembaca. Dalam hal ini, pembaca yang dikembalikan harus dibuka menggunakan CommandBehavior.CloseConnection, sehingga menutup / membuang pembaca akan menutup koneksi yang mendasarinya. Polanya terlihat seperti ini:

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

dan kode panggilan hanya perlu membuang pembaca sebagai berikut:

using(SqlDataReader reader = ExecuteReader(...))
{
    ... do your stuff ...
} // reader and connection are closed here.
Joe
sumber
Dalam potongan kode kedua di mana metode mengembalikan SqlDataReader, perintah tidak dibuang. Bolehkah dan bolehkah membuang perintah (menyertakannya dalam blok using) dan kemudian mengembalikan pembaca?
belajar
@alwayslearning persis seperti skenario yang saya miliki ...... dapatkah Anda menutup / membuang SqlCommand ketika Anda mengembalikan SqlDataReader ke pemanggil?
ganders
1
Ini buruk. Jika Anda BENAR - BENAR tidak tahan menggunakan usings maka panggil buang di finally {}blok setelah tangkapan. Dengan cara ini ditulis, perintah yang berhasil tidak akan pernah ditutup atau dibuang.
smdrager
3
@smdrager, jika Anda membaca jawabannya lebih dekat, dia berbicara tentang metode yang mengembalikan pembaca. Jika Anda menggunakan .ExecuteReader (CommandBehavior.CloseConnection); kemudian dengan membuang READER, koneksi akan terputus. Jadi metode pemanggilan hanya perlu membungkus pembaca yang dihasilkan dalam pernyataan using. menggunakan (var rdr = SqlHelper.GetReader ()) {// ...} jika Anda menutupnya di blok terakhir, maka pembaca Anda akan gagal membaca karena koneksi ditutup.
Sinaesthetic
@ganders - kembali ke posting lama ini: ya Anda bisa dan mungkin harus membuang SqlCommand - memperbarui contoh untuk melakukannya.
Joe
11

Untuk amannya, bungkus setiap objek SqlDataReader dalam pernyataan using .

Kon
sumber
Cukup adil. Namun, apakah itu benar-benar membuat perbedaan kinerja jika tidak ada pernyataan menggunakan?
Jon Ownbey
Pernyataan using sama dengan membungkus kode DataReader dalam blok try..finally ..., dengan metode tutup / buang di bagian akhirnya. Pada dasarnya, ini hanya "menjamin" bahwa benda tersebut akan dibuang dengan benar.
Todd
1
Ini langsung dari tautan yang saya berikan: "Pernyataan menggunakan memastikan bahwa Buang dipanggil bahkan jika pengecualian terjadi saat Anda memanggil metode pada objek."
Kon
6
Lanjutan ... "Anda dapat mencapai hasil yang sama dengan meletakkan objek di dalam blok percobaan dan kemudian memanggil Buang di blok akhirnya; sebenarnya, ini adalah bagaimana pernyataan using diterjemahkan oleh kompilator."
Kon
5

Cukup bungkus SQLDataReader Anda dengan pernyataan "using". Itu seharusnya menangani sebagian besar masalah Anda.

JW
sumber