5

私の質問を説明する最も簡単な方法は、次の C# コードを使用することです。

using (SqlCommand cmd = new SqlCommand("SELECT * FROM [tbl]", connectionString))
{
    using (SqlDataReader rdr = cmd.ExecuteReader())
    {
        //Somewhere at this point a concurrent thread, 
        //or another process changes the [tbl] table data

        //Begin reading
        while (rdr.Read())
        {
            //Process the data
        }
    }
}

rdrでは、そのような状況でのデータはどうなるでしょうか?

4

1 に答える 1

4

これを実際にテストしてみました。テストコード:

using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString))
{
    conn.Open();
    using (SqlCommand comm = new SqlCommand("select * from test", conn))
    {
        using (var reader = comm.ExecuteReader())
        {
            int i = 0;
            while (reader.Read())
            {
                if ((string)reader[1] == "stop")
                {
                    throw new Exception("Stop was found");
                }
            }
        }
    }
}

テストするために、いくつかのダミー データを使用してテーブルを初期化しました (値が 'stop' の行が含まれていないことを確認します)。次に、行にブレークポイントを置きint i = 0;ます。実行がブレーク ポイントで停止している間、テーブルに 'stop' 値を含む行を挿入しました。

その結果、テーブル内の最初の行の量に応じて、Exceptionスローされる/スローされないという結果になりました。行制限が正確にどこにあるのかを突き止めようとはしませんでした。10 行の場合、Exceptionはスローされませんでした。つまり、リーダーは、別のプロセスから追加された行に気付かなかったということです。10,000 行で、例外がスローされました。

したがって、答えは次のとおりです。コマンド/リーダーを でラップしTransactionないと、どちらの動作にも依存できません。

免責事項:これは私の環境でどのように機能したかです...

編集:

開発マシンでローカル Sql サーバーを使用してテストしました。次のように報告します。

Microsoft SQL Server 2008 R2 (SP1) - 10.50.2550.0 (X64)

お取引について:

トランザクションを使用するコードは次のとおりです。

using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString))
{
    conn.Open();
    using (var trans = conn.BeginTransaction())
    using (SqlCommand comm = new SqlCommand("select * from test", conn, trans))
    {
        using (var reader = comm.ExecuteReader())
        {
            int i = 0;
            while (reader.Read())
            {
                i++;
                if ((string)reader[1] == "stop")
                {
                    throw new Exception("Stop was found");
                }
            }
        }
        trans.Commit();
    }
}

このコードでは、分離レベルを明示的に指定せずにトランザクションを作成します。これは通常、それSystem.Data.IsolationLevel.ReadCommittedが使用されることを意味します (デフォルトの分離レベルは、Sql Server 設定のどこかに設定できると思います)。その場合、リーダーは以前と同じように動作します。使用するように変更した場合:

...
using (var trans = conn.BeginTransaction(System.Data.IsolationLevel.Serializable))
...

「停止」レコードの挿入は、トランザクションがコミットされるまでブロックされます。これは、リーダーがアクティブである間、基になるデータへの変更が SQL Server によって許可されないことを意味します。

于 2013-06-26T12:11:05.633 に答える