2017-07-30 11 views
1

私は、この一般的な方法があります:タスクを返す<int>はUIをフリーズしますか?

public static async Task<int> SaveChangesWithAuditingAsync<TLogType>(ObjectContext dbContext, DbSet<TLogType> logsSet, CancellationToken cancellationToken, DbChangeTracker changeTracker) 
      where TLogType : class, new() 
     { 
      ObjectContext context = ((IObjectContextAdapter)dbContext).ObjectContext; 

      await context.SaveChangesAsync(SaveOptions.DetectChangesBeforeSave, cancellationToken).ConfigureAwait(false); 


      var audits = new List<TLogType>(); 

      foreach (var entry in changeTracker.Entries().Where(o => o.State == EntityState.Modified || o.State == EntityState.Added).ToList()) 
      { 
       var changeType = entry.State.ToString(); 
       Type entityType = GetEntityType(entry); 

       string tableName = GetTableName(context, entityType); 

       var audit = new TbCommonHistoryLog 
       { 
        ObjectJson = GetEntityAsJson(entry), 
        TableName = tableName, 
       }; 
       TLogType t = new TLogType(); 
       t.ChargeFrom(audit); 
       audits.Add(t); 
      } 

      logsSet.AddRange(audits); 
      int result = 0; 
      try 
      { 
       result = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); 
       return result; 
      } 
      catch (Exception ex) 
      { 
       var m = ex.Message; 
       return result; 
      } 

     } 

私は複数のDbContextを持っている、と私はこれと同じコードを持っている必要がありますので、私はそれを作成したので、私はこの方法を保持するためにAuditHelperクラスを作成します。 DbContextはすべて同じテーブル構造(ただし名前は異なる)がTLogTypeです。 ChargeFromは、あるオブジェクトから別のオブジェクトにth値をコピーするだけです。私はオーバーライドされたのSaveChanges()メソッドからそれを呼び出すとき

問題があり、UIがフリーズ:

public override int SaveChanges() 
     { 
      return SaveChangesWithAuditingAsync(CancellationToken.None).Result; 
     } 

を私はSaveChangesの内側にそれを呼び出して、問題がないことに気づきました。 SaveChanges

asyncバージョン:

public override Task<int> SaveChangesAsync() 
     { 
      return SaveChangesWithAuditingAsync(CancellationToken.None); 
     } 

     public override Task<int> SaveChangesAsync(CancellationToken cancellationToken) 
     { 
      return SaveChangesWithAuditingAsync(cancellationToken); 
     } 

EDIT:

これはあなたがSaveChanges()SaveChangesWithAuditingAsyncにお電話を待っていないように見えますSaveChangesWithAuditingAsync

private async Task<int> SaveChangesWithAuditingAsync(CancellationToken cancellationToken) 
     { 
    return await AuditHelper.SaveChangesWithAuditingAsync(((IObjectContextAdapter)this).ObjectContext, TbHistoryLog, cancellationToken, ChangeTracker); 
     } 
+4

'.Result'は何を説明できますか?可能であれば、あなたはあなたの問題を知っています。あなたができないなら、なぜそれを使うのですか?残念なことに、これは一般的な間違いです。 –

+0

私が知っている限りでは、それはちょうど 'int'であるタスクから結果を抽出する方法です。 –

+4

はい、それはブロック的な方法で行います。だから、あなたはこれらの非同期呼び出しをすべて作って、あなたがちょうど言うことを終わらせます。ちょっと、私の結果を与えるまでブロックしてください。 'public override int SaveChanges()'は 'public override Task SaveChangesAsync()'で、 'SaveChangesWithAuditingAsync'の呼び出しを待つべきです。もちろん、これは待たなければなりません。 –

答えて

2

私は、オーバーライドのSaveChanges()メソッドからそれを呼び出すときに、UIははい、the call to Result is causing a deadlock

をフリーズします。理想的には、これを避ける最善の方法は、SaveChangesを一切呼び出さないことです。代わりにSaveChangesAsyncを使用し、SaveChangesから同期APIがサポートされていないという例外をスローします。

これが受け入れられない場合は、one of the hacks in my article on brownfield asynchronous codeを使用して調べることができます。各ハックにはそれぞれ独自の欠点があり、のシナリオでは、すべてのシナリオで動作するハックはありません。

+0

Stephenさん、 'SaveChangesAsync()'を呼び出すと、この問題が起こっていました:https://stackoverflow.com/questions/45413944/overriding-savechangesasync-when-enclosed-inside-transactionscopeそれ。回避策によって。 –

-1

である - そのウォンあなたに何か好意はありませんか?場所のいたるところに追加するのは苦痛かもしれませんが、クラッシュやハングアップをしたくない場合は必要です。

1

.Resultを使用しないでください。呼び出しスレッドで結果が利用可能になるのを待って、その後の実行をブロックします。

関連する問題