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

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


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

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