Asp.Net Core Controller에서 IAsyncEnumerable <T> 및 NotFound 반환


10

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;
    }
}

5
MyObject같은 항목 으로 여러 항목을 반품 하고 id있습니까? 일반적으로 당신은를 NotFound반환하는 무언가를 보내지 않을 것입니다 IEnumerable-그것은 비어있을 것입니다-또는 당신은 id/ 요청 된 단일 아이템을 반환 할 것 NotFound입니다.
— 골든

1
IAsyncEnumerable기다릴 수 있습니다. 사용하십시오 await foreach(var item from ThatMethodAsync()){...}.
— Panagiotis Kanavos

을 IAsyncEnumerable<MyObject>반환하려면 결과를 반환하십시오 (예 :) return objects. 그것은 하지 않습니다 비록 스트리밍 gRPC 또는 SignalR 방법에 HTTP 조치를 변환합니다. 미들웨어는 여전히 데이터를 소비하고 클라이언트에 하나의 HTTP 응답을 보내드립니다
— 파나지오티스 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의 유형 object은 ObjectResult의 Value속성에 보유됩니다 . ASP.NET Core MVC는 명령 패턴을 사용하므로 명령은 구현이며 IActionResult이를 사용하여 실행 IActionResultExecutor<T>됩니다.

를 들어 ObjectResult, ObjectResultExecutor을 설정하는 데 사용되는 ObjectResultHTTP를 응답으로. 그것은의 구현 의 ObjectResultExecutor.ExecuteAsync즉 IAsyncEnumerable<>- 인식 :

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에 대한 의견을 밝힌 것처럼이 열거는 응답이 클라이언트로 다시 전송 되기 전에 전체적으로 수행 됩니다.


자세한 답변 Kirk :)에 감사드립니다. 내가 가지고있는 한 가지 우려는 옵션 2의 액션 메소드 자체와 관련이 있습니다. 반환 값 이 비동기 적으로 열거되지만 Task객체를 반환하지 않는다는 것을 이해했습니다 . 그 사실 자체가 어떤 식 으로든 비동기 성을 방해합니까? 특히을 반환하는 비슷한 방법과 비교할 때 Task.
— 프레드릭 바보

1
아니, 괜찮아 Task메소드 자체는 비동기 작업을 수행하지 않으므로을 리턴 할 이유 가 없습니다. 위에서 설명한 것처럼 처리되는 비동기 열거입니다. 당신은 그것을 Task실행하는 곳에서 사용되는 것을 볼 수 있습니다 ObjectResult.
— 커크 라킨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.