0

.NET CoreでASP.NET MVCサイトを作成しています。私はいくつかの一般的な例外処理をカプセル化しようとしています。 Baseクラスでは、私はこのメソッドを持っています。この基本クラスから継承するコントローラからAsync/Awaitタスク操作で例外がキャッチされない

public abstract class BaseController<TController> : Controller where TController : Controller 
{ 
    protected IActionResult ExceptionHandledOperation(Func<IActionResult> operation, Func<IActionResult> handleException) 
    { 
     try 
     { 
      return operation.Invoke(); 
     } 
     catch (Exception exception) 
     { 
      Logger.LogError($"Operation {Request.Path} Exception", exception); 

      return handleException.Invoke(); 
     } 
    } 
} 

、私は同様に、この方法を利用する:

[Route("api/[controller]")] 
public class MyController : BaseController<MyController> 
{ 
    [HttpGet] 
    public IActionResult Get() 
    { 
     return ExceptionHandledOperation(() => Ok(_someService.GetAsync().Result), 
             NotFound); 
    } 
} 

は_someService.GetAsync()メソッドを想定し、このです:

public class SomeService 
{ 
    public async Task<PreconfigurationData> GetAsync() 
    { 
     // http request removed for brevity 

     if (!response.IsSuccessStatusCode) 
      throw new Exception("SomeService Exception"); 
    } 
} 

この正常に動作し、基本クラスのメソッドで私の例外をキャッチし、NotFoundの結果を返します。

しかし、私はSomeService.GetAsyncメソッドから.Resultを呼び出すことを避けたかったのです。どこでも、私はそれがデッドロックする可能性があるので、それをしないと言います。このような

public abstract class BaseController<TController> : Controller where TController : Controller 
{ 
    protected async Task<IActionResult> ExceptionHandledOperationAsync(Func<IActionResult> operation, Func<IActionResult> handleException) 
    { 
     try 
     { 
      return await Task.Run(() => operation.Invoke()); 
     } 
     catch (Exception exception) 
     { 
      Logger.LogError($"Operation {Request.Path} Exception", exception); 

      return await Task.Run(() => handleException.Invoke()); 
     } 
    } 
} 

そしてMyController:

は、だから私はこれに私のベースコントローラを修正

[Route("api/[controller]")] 
public class MyController : BaseController<MyController> 
{ 
    [HttpGet] 
    public async Task<IActionResult> Get() 
    { 
     return await ExceptionHandledOperationAsync(() => Ok(_someService.GetAsync()), 
                NotFound); 
    } 
} 

しかし、私のSomeService.GetAsyncメソッドからスローされた私の例外がキャッチされることはありませんし、私が取得することはありません例外を処理するときに送信する予定のNotFoundレスポンス。

どこでも私はあなたが試しにタスクを待つ必要があると言うと、例外が捕まえられますが、私は決してしません。


私は最終的にこれが解決取得することができた

を解決しました。 Tsengのおかげです。

+3

ASP.NET Coreアプリケーションで「Task.Run」を呼び出さないでください。あなたはそれから一つの利益を得ることはできませんし、あなたのパフォーマンスを低下させるかもしれませんし、あなたはThreadPoolの管理を台無しにするかもしれません。実際に非同期(ファイルIO、ネットワーク、データベースアクセスなど)を実行するawait/asyncのみの操作を使用します。 – Tseng

+0

https://msdn.microsoft.com/en-us/magazine/dn802603.aspx ASP.NETについてASP.NETコアへ – Tseng

+0

@Tseng Task.Runを使用せずにoperation.Invoke()を非同期にする方法を理解できません。 operation.InvokeAsync()はありません。私はTaskFactory.FromAsync(operation.BeginInvoke、operation.EndInvoke、null)を使ってみましたが、例外がスローされました。 – Ristogod

答えて

1

は、コードの論理エラーがあります。あなたはreturn await Task.Run(() => handleException.Invoke());呼び出すが、関数デリゲート内には、ここに(それを待たずに非同期コードを実行します。await ExceptionHandledOperationAsync(() => Ok(_someService.GetAsync()), NotFound)あなたのtry/catchブロック内So

は、メソッドが実行されると、すぐに非同期呼び出しが完了する前 、返し

私の推薦で指摘されているように、関数の代理人も待つ必要があります。読み込み:Taskを返します。

public abstract class BaseController<TController> : Controller where TController : Controller 
{ 
    protected async Task<IActionResult> ExceptionHandledOperationAsync<T>(
     Func<Task<T>> operation, 
     Func<object, IActionResult> successHandler, 
     Func<IActionResult> exceptionHandler 
    ) 
    { 
     try 
     { 
      return successHandler.Invoke(await operation.Invoke()); 
     } 
     catch (Exception exception) 
     { 
      //Logger.LogError($"Operation {Request.Path} Exception", exception); 

      return exceptionHandler.Invoke(); 
     } 
    } 
} 

[Route("api/[controller]")] 
public class MyController : BaseController<MyController> 
{ 
    [HttpGet] 
    public async Task<IActionResult> Get() 
    { 
     return await ExceptionHandledOperationAsync(() => _someService.GetAsync(), Ok, NotFound); 
    } 
} 
あなたは上に示した方法に(あなたがOkに結果を渡す)successHandlerすぎなどを移動する必要があります

。しかし、それは本当に醜いコードです。 Imho SomeServiceはサービス障害自体を処理し、値が見つからない場合はnullを返します。

NotFound()を返すことは非常に奇妙です。レコードが存在しないことが示唆されていますが、データのネットワーク接続またはシリアル化によって失敗する可能性があるからです。

+0

はい、NotFoundについてのあなたのコメントは正しいです。私の実際のコードはこれより複雑です。実際には、一致する型に基づいて対応するIActionResultsを返す特定の例外型のマッピングのコレクションを受け入れるオーバーライドがあります。私は例外処理を中心に解決した私の問題を示す目的で、その複雑さをすべて取り除いただけです。あなたのすべての協力に感謝します。私はやや違った動きをしていますが、代わりにあなたのパターンに切り替えるかもしれません。 – Ristogod

0

ASP.NETアプリケーションでTask.Runを使用しないという上記のコメントに同意します。つまり、あなたはInvokeメソッドをtry/catchすることができます。

例:

try 
{ 
    return await Task.Run(() => 
    { 
     try 
     { 
       operation.Invoke(); 
     } 
     catch (Exception ex) 
     { 
       // log exception 
     } 
    }); 
    } 
    catch (Exception ex) 
    { 
     // captured SynchronizationContext 
    } 
+0

私はこれを試しました。私はそれを働かせることができませんでした。おそらく、私は "取り込まれたSynchronizationContext"についてのコメントに暗黙のうちに何かが足りないと思います。私は本当にそれをどうするのか分からない。 – Ristogod

+0

これは単なるサンプルでした。 try/catchを使ってコードをラップする方法を説明しようとしていました。何が効いていないのですか? –

0

私は最終的にツェンが私に言っていたものを理解した上で、この作業を取得することができました。これは私がそれをどのように変化したかである。このような

public abstract class BaseController<TController> : Controller where TController : Controller 
{ 
    protected async Task<IActionResult> ExceptionHandledOperationAsync(Func<Task<IActionResult>> operation, Func<IActionResult> handleException) 
    { 
     try 
     { 
      return await operation.Invoke(); 
     } 
     catch (Exception exception) 
     { 
      Logger.LogError($"Operation {Request.Path} Exception", exception); 

      return handleException.Invoke(); 
     } 
    } 
} 

そしてMyController:

[Route("api/[controller]")] 
public class MyController : BaseController<MyController> 
{ 
    [HttpGet] 
    public async Task<IActionResult> Get() 
    { 
     return await ExceptionHandledOperationAsync(async() => Ok(await _someService.GetAsync()), 
                NotFound); 
    } 
} 
関連する問題