[c#] Asp.Net Core Controller에서 IAsyncEnumerable <T> 및 NotFound 반환

a IAsyncEnumerable<T>와 a 를 반환 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;
    }
}



답변

의 구현 통과 옵션 2, IAsyncEnumerable<>Ok호출은 괜찮습니다. ASP.NET Core 배관은 열거를 IAsyncEnumerable<>처리하며 3.0부터 인식됩니다.

문맥에 대해 반복되는 질문의 부름은 다음과 같습니다.

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

OkOkObjectResult(를) 상속 하는의 인스턴스 를 만드는 호출 ObjectResult입니다. 에 전달 된 값 Ok의 유형 objectObjectResultValue속성에 보유됩니다 . ASP.NET Core MVC는 명령 패턴을 사용하므로 명령은 구현이며 IActionResult이를 사용하여 실행 IActionResultExecutor<T>됩니다.

를 들어 ObjectResult, ObjectResultExecutor을 설정하는 데 사용되는 ObjectResultHTTP를 응답으로. 그것은의 구현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<>(A) 내로 열거 List<>하여 await foreach거의 정의함으로써, 요청 스레드를 차단하지 않는,. Panagiotis Kanavos가 OP에 대한 의견을 밝힌 것처럼이 열거는 응답이 클라이언트로 다시 전송 되기 전에 전체적으로 수행 됩니다.


답변