SqlDataReader'ı manuel olarak kapatmak ve imha etmek gerekli midir?


90

Burada eski kodla çalışıyorum ve birçok örnek var SqlDataReader hiçbir zaman kapatılmayan veya elden çıkarılmayan . Bağlantı kapalı ancak okuyucuyu manuel olarak yönetmenin gerekli olup olmadığından emin değilim.

Bu, performansta yavaşlamaya neden olabilir mi?

Yanıtlar:


125

Bunun gibi okuyucuları kullanmaktan kaçının:

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

Bunun yerine, bunları ifadeler kullanarak sarın:

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

Using ifadesi, nesnenin doğru bir şekilde elden çıkarılmasını ve kaynakların serbest bırakılmasını sağlayacaktır.

Eğer unutursanız, temizliği çöp toplayıcıya bırakırsınız, bu biraz zaman alabilir.


24
Her iki örnekte de .Close () ifadesine ihtiyacınız yoktur: .Dispose () çağrısı tarafından işlenir.
Joel Coehoorn

7
Muhtemelen null yerine .HasRows olup olmadığını kontrol etmek istiyorum.
JonH

3
@Andrew ExecuteReader bir istisna atarsa, nasıl null döndürebilir?
csauve

7
@JohH: örnekteki while (reader.Read ()), .HasRows ile aynı şeyi başarır ve yapmanız gerekir. Okuyucuyu ilk satıra ilerletmek için yine de okuyun.
csauve

1
@csauve Haklısın, sanırım boş dönmemiş olmalı. SqlDataReader değişkeninin değerine neden baktığımdan emin değilim.
Andrew

54

Bir SqlDataReader bertaraf SqlCommand.ExecuteReader (kullanarak örneği olduğunu Not) olacak değil yakın / altta yatan bağlantı imha edin.

İki ortak model var. İlkinde okuyucu bağlantı kapsamında açılır ve kapanır:

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

Bazen bir veri erişim yönteminin bir bağlantı açıp bir okuyucuya geri dönmesi uygun olur. Bu durumda, döndürülen okuyucunun CommandBehavior.CloseConnection kullanılarak açılması önemlidir, böylece okuyucuyu kapatmak / elden çıkarmak temeldeki bağlantıyı kapatacaktır. Desen şuna benzer:

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

ve çağıran kodun okuyucuyu bu şekilde elden çıkarması yeterlidir:

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

Yöntemin bir SqlDataReader döndürdüğü ikinci kod parçacığında, komut kullanılmaz. Bu tamam mı ve komutu elden çıkarmak (bir kullanım bloğunun içine koyun) ve ardından okuyucuyu iade etmek uygun mu?
alwayslearning

@alwayslearning bu tam olarak sahip olduğum senaryo ...... SqlDataReader'ı arayana geri döndürürken SqlCommand'i kapatabilir / atabilir misiniz?
ganders

1
Bu kötü. Eğer GERÇEKTENusing s kullanmaya dayanamıyorsanız , finally {}catch'den sonra blokta dispose arayın . Bunun yazılma şekli, başarılı komutlar asla kapatılmayacak veya atılmayacaktır.
smdrager

3
@smdrager, cevabı daha yakından okursanız, okuyucu döndüren bir yöntemden bahsediyor. .ExecuteReader (CommandBehavior.CloseConnection) kullanıyorsanız; daha sonra OKUYUCU'nun atılmasıyla bağlantı kapatılacaktır. Bu nedenle, çağırma yönteminin yalnızca sonuçta elde edilen okuyucuyu bir using deyimine sarması gerekir. (var rdr = SqlHelper.GetReader ()) {// ...} kullanarak son bloğunda kapatırsanız, okuyucunuz bağlantı kapalı olduğu için okuyamaz.
Sinaesthetic

@ganders - bu eski gönderiye geri dönersek: evet, SqlCommand'i atabilirsiniz ve muhtemelen atmalısınız - bunu yapmak için örneği güncelledi.
Joe

11

Güvende olmak için, her SqlDataReader nesnesini using deyimine sarın .


Yeterince adil. Ancak, using ifadesi yoksa, gerçekten performansta bir fark yaratır mı?
Jon Ownbey

Bir using ifadesi, DataReader kodunu bir try..finally ... bloğuna sarmaya benzer, son bölümdeki close / dispose yöntemiyle. Temel olarak, yalnızca nesnenin uygun şekilde atılacağını "garanti eder".
Todd

1
Bu, doğrudan sağladığım bağlantıdan: "using deyimi, nesnede yöntemleri çağırırken bir istisna oluşsa bile Dispose'un çağrılmasını sağlar."
Kon

6
Devam ... "Aynı sonucu, nesneyi bir try bloğunun içine yerleştirip ardından bir nihayet bloğunda Dispose çağırarak da elde edebilirsiniz; aslında, using ifadesi derleyici tarafından bu şekilde çevrilir."
Kon

5

SQLDataReader'ınızı "using" ifadesiyle sarmalamanız yeterlidir. Bu, sorunlarınızın çoğunu halletmelidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.