14

TransactionScope の使用に問題があります。TransactionScope は、データ アクセス レイヤー全体でトランザクションを使用するための非常に優れた柔軟性を提供します。このようにして、暗黙的または明示的なトランザクションを使用できます。ADO.NET トランザクションで再びパフォーマンスが向上しますが、現時点ではこれは大きな問題ではありません。ただし、ロックに問題があります。以下のコード例では、分離レベルが ReadCommitted に設定されていますが、テーブル全体がロックされているため、メイン トランザクション (Main メソッド内) がコミットされるまで、テーブル testTable に対して他のクライアントから Select SQL ステートメントを作成することはできません。また、すべてのメソッドで 1 つの接続のみを使用しようとしましたが、動作は同じでした。DBMS は SQL Server 2008 です。何かわからないことはありますか?

よろしく アントン・カルシック

このサンプル コードを参照してください。

class Program
{
    public class DAL
    {
        private const string _connectionString = @"Data Source=localhost\fsdf;Initial Catalog=fasdfsa;Integrated Security=SSPI;";

        private const string inserttStr = @"INSERT INTO dbo.testTable (test) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }
    }

    static void Main(string[] args)
    {

        DAL dal = new DAL();
        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

        using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required,tso))
        {
            dal.FirstMethod();
            dal.SecondMethod();
            sc.Complete();
        }
    }
}
4

2 に答える 2

19

あなたの問題は .NET TransactionScope の概念とは何の関係もないと思います。むしろ、SQL Server トランザクションの予想される動作を説明しているように思えます。また、分離レベルの変更は、「データ書き込み」ではなく「データ読み取り」にのみ影響します。SQL Server BOL から:

「トランザクション分離レベルを選択しても、データ変更を保護するために取得されるロックには影響しません。トランザクションは、変更するすべてのデータに対して常に排他的ロックを取得し、そのトランザクションに設定された分離レベルに関係なく、トランザクションが完了するまでそのロックを保持します。読み取り操作、トランザクション分離レベルは主に、他のトランザクションによる変更の影響からの保護レベルを定義します。」

つまり、ステートメントを発行するクライアントの分離レベルを変更することで、ブロッキング動作を防ぐことができます。SELECT分離レベル(READ COMMITEDデフォルト) はブロッキングを妨げません。クライアントをブロックしないようにするには、READ UNCOMMITTED分離レベルを使用しますが、開いているトランザクションによって更新/挿入されたレコードが取得される可能性を考慮する必要があります (つまり、トランザクションがロールバックするとレコードが消える可能性があります)。

于 2009-04-06T19:32:11.777 に答える
9

トランザクションについて話すのに良い質問です。

主な方法は、トランザクションをコミットし続けることです。他のメソッド内でコミットした場合でも、その行はロックされたままになります。ロックトランザクションをコミットするまで、READCOMMITTEDを使用してそのテーブルを読み取ることはできません。これは予期されていることです。

これが最初のメソッドが戻った後です:

最初のメソッドは

2番目のメソッドが戻った後、テーブルにもう1つのロックを追加します。

secontメソッドは

SPID(55)を使用してクエリウィンドウからselectステートメントを実行すると、待機ステータスが表示されます。

選択が待機しています

メインメソッドのトランスコミット後、selectステートメントの結果が表示され、selectステートメントのクエリページからのみ共有ロックが表示されます。

スコープはコミットし、リターンを選択します

Xは排他ロック、IXインテントロックを意味します。トランザクションに関する私のブログ投稿から詳細を読むことができます

待たずに読みたい場合は、nolockヒントを使用できます。最初のメソッドがコミットした後に読み取りたい場合は、その外部スコープを削除できます。

于 2012-04-18T15:12:57.630 に答える