SqlDataReaderを手動で閉じて破棄する必要がありますか?


90

私はここでレガシーコードを扱っていますがSqlDataReader、閉じられたり破棄されたりすることのないインスタンスがたくさんあります。接続は閉じられていますが、リーダーを手動で管理する必要があるかどうかはわかりません。

これによりパフォーマンスが低下する可能性はありますか?

回答:


124

次のようなリーダーの使用は避けてください。

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

usingステートメントは、オブジェクトの正しい廃棄とリソースの解放を保証します。

忘れた場合は、ガベージコレクタにクリーニングを任せます。これには時間がかかる場合があります。


24
どちらのサンプルでも.Close()ステートメントは必要ありません。.Dispose()呼び出しによって処理されます。
Joel Coehoorn、2009

7
おそらくそれがnullではなく.HasRowsであるかどうかをチェックしたいでしょう。
JonH 2009年

3
@Andrew ExecuteReaderが例外をスローした場合、どのようにnullを返すことができますか?
csauve

7
@JohH:例のwhile(reader.Read())は.HasRowsと同じように実行されます。リーダーを最初の行に進めるには、とにかく.Readが必要です。
csauve

1
@csauveそうですね、nullを返してはいけません。SqlDataReader変数の値を確認した理由がわかりません。
Andrew

53

SqlCommand.ExecuteReader()を使用してインスタンス化されたSqlDataReaderを破棄しても、基になる接続閉じられない / 破棄されないことに注意してください。

2つの一般的なパターンがあります。最初に、リーダーは接続の範囲内で開かれ、閉じられます。

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を返す2番目のコードスニペットでは、コマンドは破棄されません。それは大丈夫ですか?コマンドを破棄して(usingブロックで囲んで)リーダーを返すことは問題ありませんか?
常に

@alwayslearningこれはまさに私が持っているシナリオです......呼び出し元にSqlDataReaderを返すときに、SqlCommandを閉じたり破棄したりできますか?
12

1
これは悪いです。本当にusing sの使用に耐えられない場合は、finally {}キャッチ後にブロックでdisposeを呼び出します。これが書かれている方法では、成功したコマンドは決して閉じられたり破棄されたりしません。
smdrager 2012年

2
@smdrager、あなたが答えをよく読んだ場合、彼はリーダーを返すメソッドについて話している。.ExecuteReader(CommandBehavior.CloseConnection);を使用する場合 その後、READERを破棄することにより、接続が閉じられます。したがって、呼び出しメソッドは、結果のリーダーをusingステートメントでラップするだけで済みます。using(var rdr = SqlHelper.GetReader()){// ...} finallyブロックで閉じている場合、接続が閉じているため、リーダーは読み取りに失敗します。
Sinaesthetic

@ganders-この古い投稿に戻ります:はい、SqlCommandを破棄できますし、おそらく破棄する必要があります-そのように例を更新しました。
ジョー

11

安全のために、すべてのSqlDataReaderオブジェクトをusingステートメントでラップします。


けっこうだ。ただし、usingステートメントがない場合、実際にはパフォーマンスに違いがありますか?
Jon Ownbey 2009

usingステートメントは、DataReaderコードをtry..finally ...ブロックでラップするのと同じで、finallyセクションにclose / disposeメソッドがあります。基本的には、オブジェクトが適切に破棄されることを「保証する」だけです。
トッド

これは、私が提供したリンクから直接です:「usingステートメントは、オブジェクトのメソッドを呼び出している間に例外が発生した場合でも、Disposeが呼び出されることを保証します。」
Kon

5
続き...「tryブロック内にオブジェクトを配置し、finallyブロックでDisposeを呼び出すことで同じ結果を得ることができます。実際、これは、usingステートメントがコンパイラーによって変換される方法です。」
Kon

5

SQLDataReaderを "using"ステートメントでラップするだけです。これでほとんどの問題が解決されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.