6

次のアプローチを使用して、データベースに対してクエリを実行し、データを読み取ります。

using(SqlConnection connection = new SqlConnection("Connection string"))
{
    connection.Open();

    using(SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection))
    {
        using (SqlDataReader reader = command.ExecuteReader())
        {
              // read and process data somehow (possible source of exceptions)
        } // <- reader hangs here if exception occurs
    } 
}

データの読み取りおよび処理中に、いくつかの例外が発生する可能性があります。問題は、呼び出し時に例外がスローDataReaderされてハングする場合です。Close()理由はありますか?そして、この問題を適切な方法で解決するにはどうすればよいですか?try..catch..finallyの代わりにブロックを書き、リーダーを破棄する前にusing呼び出すと、問題はなくなりました。command.Cancel()finally

作業バージョン:

    using(SqlConnection connection = new SqlConnection("Connection string"))
    {
        connection.Open();

        using(SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection))
        {
            SqlDataReader reader = command.ExecuteReader();
            try
            {
                // read and process data somehow (possible source of exceptions)
            }
            catch(Exception ex)
            {
                // handle exception somehow
            }
            finally
            {
               command.Cancel(); // !!!
               reader.Dispose();
            }
        } 
    }
4

3 に答える 3

8

例外が発生すると、すべてのデータを受信する前にデータの処理を停止します。数行後に処理を中止すると、例外がなくてもこの問題を再現できます。

コマンドまたはリーダーが破棄されても、クエリはサーバー上で引き続き実行されます。ADO.NET は、残りのすべての行と結果セットを狂ったように読み取り、それらを破棄します。これは、サーバーがそれらを送信しており、プロトコルがそれらを受信する必要があるためです。

呼び出しSqlCommand.Cancelにより、SQL Server に「注意」が送信され、クエリが実際に中止されます。SSMSでキャンセルボタンを押すのと同じことです。

要約すると、この問題は、さらに多くの行が受信されているにもかかわらず、行の処理を停止するたびに発生します。あなたの回避策(呼び出しSqlCommand.Cancel)が正しい解決策です。

于 2013-10-30T17:10:16.863 に答える
2

Disposeの方法についてSqlDataReader、MSDN (リンク) には次のように書かれています。

DbDataReader によって使用されているリソースを解放し、Close を呼び出します

強調は私が追加しました。その後、Closeメソッド ( link ) を見ると、次のように述べられています。

Close メソッドは、出力パラメーター、戻り値、および RecordsAffected の値を入力するため、大規模または複雑なクエリの処理に使用された SqlDataReader を閉じるのにかかる時間が長くなります。クエリによって影響を受ける戻り値とレコード数が重要でない場合、Close メソッドを呼び出す前に、関連付けられた SqlCommand オブジェクトの Cancel メソッドを呼び出すことで、SqlDataReader を閉じるのにかかる時間を短縮できます。

したがって、リーダーの繰り返し処理を停止する必要がある場合は、作業中のバージョンと同じように、最初にコマンドをキャンセルすることをお勧めします。

于 2013-10-30T17:17:06.610 に答える
-2

私はそれをそのようにフォーマットしません。
開ける(); try ブロック内になく、例外
ExecuteReader();をスローする可能性があります。try ブロックに含まれておらず、
reader.Close が好きな例外をスローする可能性があります - これは MSDN サンプルで見られる原因であり、
数字があるため (タイムアウトなど) SQLexception をキャッチします。

SqlConnection connection = new SqlConnection();
SqlDataReader reader = null;
try
{
    connection.Open();  // you are missing this as a possible source of exceptions
    SqlCommand command = new SqlCommand("SELECT * FROM TableName", connection);
    reader = command.ExecuteReader();  // you are missing this as a possible source of exceptions
    // read and process data somehow (possible source of exceptions)
}
catch (SqlException ex)
{
}
catch (Exception ex)
{
    // handle exception somehow
}
finally
{
    if (reader != null) reader.Close();
    connection.Close();
}
于 2013-10-30T15:58:44.160 に答える