2011-10-07 20 views
5

私はバッチ処理システムを構築しています。 Unitsのバッチは、20〜1000の量で入っています。各Unitは基本的にモデルの階層(1つのメインモデルと多くの子モデル)です。私の仕事は、各モデル階層をデータベースに単一のトランザクションとして保存することです(各階層がコミットするかロールバックされます)。残念ながら、EFは、数千のレコードを格納する可能性があるため、モデル階層の2つの部分を処理できませんでした。EF競合するSaveChanges()コール

これを解決するために、SqlBulkCopyを設定して、これらの2つの可能性の高い高カウントモデルを処理し、EFが残りの挿入(および参照整合性)を処理させるようにしました。

バッチループ:

foreach (var unitDetails in BatchUnits) 
{ 
    var unitOfWork = new Unit(unitDetails); 
    Task.Factory.StartNew(() => 
    { 
     unitOfWork.ProcessX(); // data preparation 
     unitOfWork.ProcessY(); // data preparation 
     unitOfWork.PersistCase(); 
    }); 
} 

単位:

class Unit 
{ 
    public PersistCase() 
    { 
    using (var dbContext = new CustomDbContext()) 
    { 
     // Need an explicit transaction so that 
     // EF + SqlBulkCopy act as a single block 
     using (var scope = new TransactionScope(TransactionScopeOption.Required, 
     new TransactionOptions() { 
      IsolationLevel = System.Transaction.IsolationLevel.ReadCommitted 
     })) 
     { 
     // Let EF Insert most of the records 
     // Note Insert is all it is doing, no update or delete 
     dbContext.Units.Add(thisUnit); 
     dbContext.SaveChanges(); // deadlocks, DbConcurrencyExceptions here 

     // Copy Auto Inc Generated Id (set by EF) to DataTables 
     // for referential integrity of SqlBulkCopy inserts 
     CopyGeneratedId(thisUnit.AutoIncrementedId, dataTables); 

     // Execute SqlBulkCopy for potentially numerous model #1 
     SqlBulkCopy bulkCopy1 = new SqlBulkCopy(...); 
     ... 
     bulkCopy1.WriteToServer(dataTables["#1"]); 

     // Execute SqlBulkCopy for potentially number model #2 
     SqlBulkCopy bulkCopy2 = new SqlBulkCopy(...); 
     ... 
     bulkCopy2.WriteToServer(dataTables["#2"]); 

     // Commit transaction 
     scope.Complete(); 
     } 
    } 
    } 
} 

今、私は基本的にロックやハードの場所の間で立ち往生しています。 IsolationLevelReadCommittedに設定したままでは、TasksEFINSERTステートメントの間にデッドロックが発生します。

私はDbConcurrencyExceptionsを取得する(私はSELECTsをやっていないよので、私は大丈夫だろうと思っている)ReadUncommittedIsolationLevelを設定した場合。

私はDbConcurrencyExceptionsEntity Frameworkについての良い情報を見つけることができませんされてきたが、私はReadUncommittedは、本質的に情報無効「挿入された行」を受信するEFを引き起こしていることを推測しています。

UPDATEはここ

がINSERTSをしながら実際に私のデッドロックの問題を引き起こしているかについて、いくつかの背景情報がある:ときLINQのはへ

http://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005

どうやらこの同じ問題は数年前に存在していましたSQLが出てきて、Microsoftはscope_identity()の選択方法を変更することでそれを修正しました。 Entity Frameworkで同じ問題が発生したときにSQL Serverの問題になっている理由がわからない。

+0

_competing_または_completing_? –

答えて

3

この問題は、ここではかなりよく説明されています。基本的にhttp://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005

その内部EFの問題。 Linq To SQLを使用するようにコードを移行しましたが、現在は正常に動作します(ID値として不要なSELECTは必要ありません)。固定されたLINQのためのSQLの正確な同じ問題から

関連引用:

テーブルがID列を有し、SQLにLINQのは、テーブルに挿入するための極めて 非効率的なSQLを生成します。テーブルが のOrderで、identiyカラムがIdであるとします。生成されるSQLは:

exec sp_executesql N'INSERT INTO [dbo]です。[注文]([Colum1]、[Column2]) VALUES(@ p0、@ p1)

SELECT [t0] FROM [dbo]。[AS] [t0] WHERE [t0]。 [ID] = (SCOPE_IDENTITY())」、N '@ P0 INT、@ P1 INT、@ P0 = 124、@ P1 = 432

つSCOPE_IDENTITYを(返すのではなく、見ることができるように)直接 を使用して' SELECT SCOPE_IDENTITY() 'の場合、生成されたSQLは、SCOPE_IDENTITY()によって返された値を使用して、 Id列のSELECTを実行します。テーブル内のレコード数が である場合、これにより挿​​入が大幅に遅くなります( )。テーブルが分割されると、問題はさらに悪化して になります。

関連する問題