2011-10-26 12 views
3

では使用できません:で実行されている他のスレッドがあるのでEF4.1 + MARS - 新しいトランザクション私は、データベース内のエンティティを更新していたときに、私は次の例外を取得していますUPDATE

新しいトランザクションは許可されていませんセッション。

同じコードブロックを使用して、すべての受信データを処理します(Excelワークブックからスクレイプされています)。ここで、また、

public void UploadWorkbook(Workbook workbook, string uploadedBy, string comments) 
{ 
    workbook.UploadedBy = uploadedBy; 
    workbook.Comments = comments; 

    var existing = _repository.Get(x => x.FileName == workbook.FileName); 
    if (existing == null) 
     _repository.Insert(workbook); // this works on the commit 
    else 
     _repository.Update(workbook); // this causes commit to fail 

    _unitOfWork.Commit(); // fails when repository runs update method 
} 

更新方法である:作業INSERTと動作しないUPDATEの唯一の違いは以下のとおりです。

:ここ
public override void Update(Workbook entity) 
{ 
    var existing = Get(x => x.FileName == entity.FileName); 

    if (existing == null) 
    { 
     var message = string.Format(@"Error :: Cannot update Workbook '{0}' 
            because it does not exist in the database.", 
            entity.FileName); 

     throw new Exception(message); 
    } 

    existing.Version = entity.Version; 
    existing.DateModified = DateTime.Now; 
    existing.Comments = entity.Comments; 
    existing.FilePath = entity.FilePath; 

    if (existing.Data != null) 
     existing.Data.Clear(); 

    existing.Data = entity.Data; 
} 

は、Getメソッドの実装であります
public virtual T Get(Func<T, bool> where) 
{ 
    return _dbSet.Where(where).FirstOrDefault(); 
} 

私は類似の問題に関して他のいくつかの記事を見てきましたが、私が経験しているものと全く同じものはありませんでした。私は本当にINSERTがどのように動作するのか分からないので、ここでは本当に立ち往生していますが、UPDATEは失敗します。別のトランザクションがあった場合、どちらのアクションも失敗するでしょうか?

+0

どのようにあなたのコンテキスト/リポジトリ/セットの寿命を扱うのですか?同時に実行する操作の数はいくつですか? –

+0

@LadislavMrnka私のコンテキストとリポジトリの有効期間はHTTP要求ごとであり、私は考えることができる並行操作はありません。私がMARSを有効にしている唯一の理由は、遅延データの読み込みとネストされたクエリの実行が、「既に開いているデータリーダーがある」という例外を避けるためです。 – shuniar

答えて

3

私の最初の疑惑は、あなたのGet呼び出し

Get(x => x.FileName == entity.FileName); 

が暗黙のうちに作業単位をコミットする前に閉じられていない新しいスレッド/トランザクションを作成することになります。次に、あなたの作業ユニットは、既にオープンしているGet()トランザクションと競合する、コミットの呼び出しで全く新しいトランザクションを作成しようとしています。

これが当てはまる場合、Getコールをコミット呼び出しと同じトランザクションで動作させる方法を理解したいと思うかもしれません。

編集:私はあなたが単純であることを終了するシーケンスを強制すべきである

public virtual T Get(Func<T, bool> where) 
{ 
    return _dbSet.Where(where).SingleOrDefault(); 
} 

SingleOrDefault()

public virtual T Get(Func<T, bool> where) 
{ 
    return _dbSet.Where(where).FirstOrDefault(); 
} 

を変更することで、あなたの問題を解決することができるかもしれ信じる

「読みコミットトランザクションの接続を解放します。あなたのクエリが複数の結果を返した場合、順序が指定されていないので、実際にどのレコードを取得するかは、FirstOrDefault()から明らかではありません。SingleOrDefault()を使用すると、複数の行が返されます)

それとも、明示的なトランザクションのスコープを試みることができる:

public void UploadWorkbook(Workbook workbook, string uploadedBy, string comments) 
{ 
    workbook.UploadedBy = uploadedBy; 
    workbook.Comments = comments; 

    using (var transaction = new TransactionScope()) 
    { 
     var existing = _repository.Get(x => x.FileName == workbook.FileName); 
     if (existing == null) 
      _repository.Insert(workbook); // this works on the commit 
     else 
      _repository.Update(workbook); // this causes commit to fail 

     _unitOfWork.Commit(); // fails when repository runs update method 
     transaction.Complete(); 
    } 
} 

(もMicrosoft Connect page on this error messageを参照してください)

+0

ご回答いただきありがとうございます。上記の 'Get()'メソッドの実装を追加しました。 – shuniar

+0

私はGet()のimplementatinoを見ていますが、もう少し詳細が必要だと思います。「_dbSet」の型は何ですか?どのように初期化されますか? – Nathan

+0

_dbSetは 'IDbSet '型で、リポジトリコンストラクタで、 'DbSet 'の代わりに 'IDbSet 'を返すようにオーバーライドした '_dbSet = DbContext.Set ();'で初期化されています。また、リポジトリはDatabaseFactoryを使用してDbContextを作成します。 – shuniar