4

Sql Server CE 4 と Entity Framework およびSystem.Transactions.TransactionScope.

以下の単純化されたコードは、問題を示す単体テストからのものです。

アイデアは、ブロック (「周囲の」トランザクション)innerScopeに影響を与えることなく、ブロック (トランザクションなし) が成功または失敗できるようにすることです。outerScopeこれが の目的ですTransactionScopeOption.Suppress

SomeTableただし、 への最初の挿入によってテーブル全体がロックされているように見えるため、コードは失敗しますouterScope。コードに示されている時点で、次のエラーがスローされます。

"SQL Server Compact がロックの待機中にタイムアウトしました。既定のロック時間は、デバイスの場合は 2000 ミリ秒、デスクトップの場合は 5000 ミリ秒です。既定のロック タイムアウトは、接続文字列で ssce: default lock timeout プロパティを使用して増やすことができます。[セッション ID = 2、スレッド ID = 2248、プロセス ID = 13516、テーブル名 = SomeTable、競合タイプ = x ロック (x ブロック)、リソース = PAG (idx): 1046 ]"

[TestMethod()]
[DeploymentItem("MyLocalDb.sdf")]
public void MyLocalDb_TransactionSuppressed()
{
    int count = 0;

    // This is the ambient transaction
    using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required))
    {
        using (MyObjectContext outerContext = new MyObjectContext())
        {
            // Do something in the outer scope
            outerContext.Connection.Open();
            outerContext.AddToSomeTable(CreateSomeTableRow());
            outerContext.SaveChanges();
            try
            {
                // Ambient transaction is suppressed for the inner scope of SQLCE operations
                using (TransactionScope innerScope = new TransactionScope(TransactionScopeOption.Suppress))
                {
                    using (MyObjectContext innerContext = new MyObjectContext())
                    {
                        innerContext.Connection.Open();
                        // This insert will work
                        innerContext.AddToSomeTable(CreateSomeTableRow());
                        innerContext.SaveChanges(); // ====> EXCEPTION THROWN HERE
                        // There will be other, possibly failing operations here
                    }
                    innerScope.Complete();
                }
            }
            catch { }
        }
        outerScope.Complete();
    }

    count = GetCountFromSomeTable();
    // The insert in the outer scope should succeed, and the one from the inner scope
    Assert.AreEqual(2, count);
}

そのため、 http://msdn.microsoft.com/en-us/library/ms172001によると、「トランザクション スコープ内のトランザクションは分離レベルを Serializable に設定して実行される」ようです。

ただし、次のコード スニペットを使用して TransactionScope の分離レベルを変更しても役に立ちません。

public void MyLocalDb_TransactionSuppressed()
{
    TransactionOptions opts = new TransactionOptions();
    opts.IsolationLevel = IsolationLevel.ReadCommitted;
    int count = 0;

    // This is the ambient transaction
    using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required, opts))
    ...

同じ場所で同じ例外がスローされます。

これを回避する唯一の方法は、ブロックouterScope.Complete()に入る前に呼び出すことです。innerScopeしかし、これは目的に反します。

ここで何が欠けていますか?ありがとう。

4

2 に答える 2

1

AFAIK SQL Server Compact はネストされたトランザクションをサポートしていません。

于 2012-05-30T09:30:36.503 に答える
0

そして、なぜあなたはこのようにするのですか?あなたのコードを見ると、最初のトランザクションスコープ内で2番目のトランザクションスコープを実行することと、それらを順番に実行することに違いはありません。

IMHO これは、SQL CompactTransactionScopeや分離レベルの問題ではありません。これは、間違ったアプリケーション ロジックの問題です。

それぞれがトランザクションで実行されます -または内部SaveChangesで定義された外部トランザクションのいずれかです。トランザクションを作成しない場合でも、すべてのデータベース コマンドには独自の暗黙のトランザクションがあります。内部コード ブロックで Suppress を使用すると、同じテーブルに挿入しようとする 2 つの同時トランザクションが作成されます。さらに、最初のトランザクションは 2 番目のトランザクションを完了しないと完了できず、2 番目のトランザクションは最初の => デッドロックを完了しないと完了できません。TransactionScopeDbTransaction

その理由は、挿入コマンドが常にテーブルの一部をロックし、コミットまたはロールバックされるまで新しい挿入を許可しないためです。トランザクション分離レベルを変更することでこれを回避できるかどうかはわかりません。回避できる場合は、おそらくRead.Uncommitted.

于 2012-05-30T09:43:00.140 に答える