web-dev-qa-db-ja.com

Startup.Configureで非同期操作を処理するにはどうすればよいですか?

ASP.NET 5アプリで、一部のデータをAzureからStartup.Configureメソッド内のキャッシュに読み込みます。 Azure SDKは、非同期メソッドのみを公開します。通常、非同期メソッドの呼び出しは、次のように非同期メソッド内のawaitを介して行われます。

public async Task Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Data dataToCache = await DataSource.LoadDataAsync();
    cache.Set("somekey", dataToCache);

    // remainder of Configure method omitted for clarity
}

ただし、ASP.NET 5では、Configureメソッドがvoidを返す必要があります。非同期voidメソッドを使用できますが、私の理解では、非同期voidメソッドはイベントハンドラーにのみ使用されることになっています( https://msdn.Microsoft.com/en-us/magazine/jj991977。 aspx その他)。

これを行うためのより良い方法は、awaitなしで非同期関数を呼び出し、返されたタスクでWaitを呼び出し、Task.Resultsプロパティを介して結果をキャッシュすることだと考えていました:

public void Configure(IApplicationBuilder app, IMemoryCache cache)
{
    Task<Data> loadDataTask = DataSource.LoadDataAsync();
    loadDataTask.Wait();
    cache.Set("somekey", loadDataTask.Result);

    // remainder of Configure method omitted for clarity
}

Stephen Waltherは、今年初めに ブログ投稿 で同様のアプローチを使用しました。ただし、これが容認できる慣行と見なされるかどうかは、その投稿からは明らかではありません。それは...ですか?

これが容認できる慣行であると考えられる場合、エラー処理があれば-何が必要ですか?私の理解では、Task.Wait()は非同期操作によってスローされた例外を再スローし、非同期操作をキャンセルするメカニズムは提供していません。 Task.Wait()を呼び出すだけで十分ですか?

62
DevHawk

リンクしたブログのサンプルコードは、sync-over-asyncのみを使用してデータベースにサンプルデータを入力していました。その呼び出しは本番アプリには存在しません。

まず、Configureを非同期にする必要がある場合は、ASP.NETチームに問題を提起する必要があるため、ASP.NETチームの注目を集める必要があります。この時点で(つまり、リリース前に)ConfigureAsyncのサポートを追加するのはそれほど難しくありません。

第二に、問題へのアプローチがいくつかあります。 couldは_task.Wait_を使用します(または、エラーが発生した場合にAggregateExceptionラッパーを回避するtask.GetAwaiter().GetResult()を使用することをお勧めします)。または、タスクのresultではなくtaskをキャッシュすることもできます(IMemoryCacheが奇妙なシリアライズよりも辞書の場合に機能します-メモリ内のバイナリ配列-ASP.NETの以前のバージョンを見ています。

これが容認できる慣行であると考えられる場合、エラー処理があれば-何が必要ですか?

GetAwaiter().GetResult()を使用すると、例外が(もしあれば)Configureから伝播します。 configuringアプリケーションが失敗した場合、ASP.NETがどのように応答するかはわかりません。

非同期操作をキャンセルするメカニズムを提供していません。

applicationのセットアップをどのように「キャンセル」できるのかわかりませんので、その部分については心配しません。

31
Stephen Cleary

非同期作業を行うことはできますが、メソッドは同期であり、変更することはできません。つまり、非同期呼び出しが完了するまで同期的に待機する必要があります。

スタートアップがまだ終了していない場合、スタートアップメソッドから戻りたくありませんか?あなたの解決策は大丈夫のようです。

例外処理に関しては、アプリケーションがなければ適切に実行できない作業がある場合、Startupメソッドを失敗させる必要があります( Fail-fast を参照)。重要でない場合は、関連する部分をtry catchブロックで囲み、後で検査するために問題を記録します。

1
mbudnik

ここでの答えは、非同期コードがさらに非同期呼び出しを行う場合、特にそれらがコールバックである場合、常に正しく機能するとは限りません。その場合、コードのデッドロックが見つかる可能性があります。

これは私にとって何度も起こり、 Nito.AsyncEx 効果が大きい。

using Nito.AsyncEx;

AsyncContext.Run(async () => { await myThing.DoAsyncTask(); });
0
Chris