Asp.NetコアコントローラーからIAsyncEnumerable <T>およびNotFoundを返す


10

IAsyncEnumerable<T>and を返すが、NotFoundResultまだ非同期で処理されるコントローラーアクションの正しいシグネチャは何ですか?

私はこのシグネチャを使用しましたが、それがIAsyncEnumerable<T>待てないためコンパイルできません:

[HttpGet]
public async Task<IActionResult> GetAll(Guid id)
{
    try
    {
        return Ok(await repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
    }
    catch (NotFoundException e)
    {
        return NotFound(e.Message);
    }
}

これは正常にコンパイルされますが、その署名は非同期ではありません。だから私はそれがスレッドプールのスレッドをブロックするかどうか心配しています:

[HttpGet]
public IActionResult GetAll(Guid id)
{
    try
    {
        return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
    }
    catch (NotFoundException e)
    {
        return NotFound(e.Message);
    }
}

私はawait foreachこのようにループを使用しようとしましたが、それも明らかにコンパイルされません:

[HttpGet]
public async IAsyncEnumerable<MyObject> GetAll(Guid id)
{
    IAsyncEnumerable<MyObject> objects;
    try
    {
        objects = contentDeliveryManagementService.GetAll(id); // GetAll() returns an IAsyncEnumerable
    }
    catch (DeviceNotFoundException e)
    {
        return NotFound(e.Message);
    }

    await foreach (var obj in objects)
    {
        yield return obj;
    }
}

5
あなたはMyObject同じで複数のアイテムを返していますidか?通常はNotFound、何かを返すものに対してはを送信しませんIEnumerable-それは単に空である-または要求されたid/を持つ単一のアイテムを返しますNotFound
crgolden

1
IAsyncEnumerableお待ちしております。を使用しawait foreach(var item from ThatMethodAsync()){...}ます。
Panagiotis Kanavos

戻りたい場合はIAsyncEnumerable<MyObject>、単に結果を返しますreturn objects。それはないだろうけれども、ストリーミングgRPCまたはSignalRメソッドにHTTPアクションを変換します。ミドルウェアは引き続きデータを消費し、単一のHTTP応答をクライアントに送信します
Panagiotis Kanavos

オプション2は問題ありません。ASP.NET CoreのIAsyncEnumerableプラミングは列挙を処理し、3.0以降は認識されます。
カークラーキン

みんなありがとう。ここでは404を返さないことはわかっていますが、これは単なる不自然な例です。実際のコードはかなり異なります。@KirkLarkinは害虫になって申し訳ありませんが、これによりブロッキングが発生しないことを100%確信していますか?はいの場合、オプション2が明白な解決策です。
フレデリック

回答:


6

実装を通過オプション2、IAsyncEnumerable<>へのOkコールは、結構です。ASP.NET CoreのIAsyncEnumerable<>プラミングは列挙を処理し、3.0以降は認識されます。

これは質問からの呼び出しであり、コンテキストについて繰り返されます:

return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable

の呼び出しOkによりOkObjectResult、を継承するのインスタンスが作成されますObjectResult。に渡される値OkはのタイプでobjectObjectResultValueプロパティに保持されます。ASP.NET Core MVCはコマンドパターンを使用します。これにより、コマンドはの実装でIActionResultあり、の実装を使用して実行されIActionResultExecutor<T>ます。

の場合ObjectResult、をHTTP応答に変換するためにObjectResultExecutor使用されObjectResultます。それは-awareの実装です:ObjectResultExecutor.ExecuteAsyncIAsyncEnumerable<>

public virtual Task ExecuteAsync(ActionContext context, ObjectResult result)
{
    // ...

    var value = result.Value;

    if (value != null && _asyncEnumerableReaderFactory.TryGetReader(value.GetType(), out var reader))
    {
        return ExecuteAsyncEnumerable(context, result, value, reader);
    }

    return ExecuteAsyncCore(context, result, objectType, value);
}

コードが示すように、Valueプロパティが実装されているかどうかがチェックされますIAsyncEnumerable<>(詳細はへの呼び出しで非表示になっていますTryGetReader)。含まれている場合は、ExecuteAsyncEnumerable呼び出され、列挙が実行され、列挙された結果がに渡されExecuteAsyncCoreます。

private async Task ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, object asyncEnumerable, Func<object, Task<ICollection>> reader)
{
    Log.BufferingAsyncEnumerable(Logger, asyncEnumerable);

    var enumerated = await reader(asyncEnumerable);
    await ExecuteAsyncCore(context, result, enumerated.GetType(), enumerated);
}

reader上記のスニペットでは、列挙が行われます。少し埋め込まれていますが、ソースはここに表示されます

private async Task<ICollection> ReadInternal<T>(object value)
{
    var asyncEnumerable = (IAsyncEnumerable<T>)value;
    var result = new List<T>();
    var count = 0;

    await foreach (var item in asyncEnumerable)
    {
        if (count++ >= _mvcOptions.MaxIAsyncEnumerableBufferLimit)
        {
            throw new InvalidOperationException(Resources.FormatObjectResultExecutor_MaxEnumerationExceeded(
                nameof(AsyncEnumerableReader),
                value.GetType()));
        }

        result.Add(item);
    }

    return result;
}

IAsyncEnumerable<>列挙されているList<>使用してawait foreachほぼ定義することによって、要求スレッドをブロックしません、、。Panagiotis KanavosがOPのコメントで呼びかけたように、この列挙は応答がクライアントに返されるに完全実行されます。


詳細な回答のおかげでカーク:)。オプション2のアクションメソッド自体に懸念があります。その戻り値は非同期で列挙されることを理解しましたが、Taskオブジェクトを返しません。その事実自体が何らかの方法で非同期性を妨げていますか?特に、たとえば、を返す同様のメソッドと比較してくださいTask
フレデリックザフール

1
いいえ、大丈夫です。Taskメソッド自体は非同期処理を実行しないため、を返す理由はありません。これは非同期である列挙型であり、上記のように処理されます。Taskの実行で、それがそこで使用されていることがわかりObjectResultます。
カークラーキン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.