9

I have a controller method of this signature:

public async IAsyncEnumerable<MyDto> Get()

It works fine but I need to do some request validation and return 401, 400, and other codes accordingly, which it does not support. Alternatively, the following signature does not compile:

public async Task<ActionResult<IAsyncEnumerable<MyDto>>> Get()

Error:

Cannot implicitly convert type 'Microsoft.AspNetCore.Mvc.UnauthorizedResult' to 'MyApi.Responses.MyDto'

The full method:

public async IAsyncEnumerable<MyDto> Get()
{
    if (IsRequestInvalid())
    {
        // Can't do the following. Does not compile.
        yield return Unauthorized();
    }
    var retrievedDtos = _someService.GetAllDtosAsync(_userId);

    await foreach (var currentDto in retrievedDtos)
    {
        yield return currentDto;
    }
}

Any ideas? Can't seem to believe that Microsoft has designed IAsyncEnumerable to be used without the possibility/flexibility of returning anything else.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Abubakar Mehmood
  • 938
  • 1
  • 10
  • 19
  • This has little to do with `IAsyncEnumerable`. If you used `async Task` you'd have the same problem. If you want to return specific responses, return `IActionResult` or `ActionResult` – Panagiotis Kanavos Mar 09 '20 at 13:47
  • This is explained [in the docs](https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1#specific-type_): `In such a case, it's common to mix an ActionResult return type with the primitive or complex return type. Either IActionResult or ActionResult are necessary to accommodate this type of action.` – Panagiotis Kanavos Mar 09 '20 at 13:52
  • 3
    @PanagiotisKanavos It's not the same problem because in case of Task, I can easily make it `Task>` whereas I cannot do `Task>>` (as mentioned in the question). And I need IAsyncEnumerable to pass the results to the serializer as they arrive. – Abubakar Mehmood Mar 10 '20 at 06:52
  • 2
    It's *exactly* the same problem - unless you return an `ActionResult` or `IActionResult`, you can't return a status. The question is how to return that, *and* keep the benefits of IAsyncEnumerable. Looking at [the source for ObjectResultExecutor](https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Infrastructure/ObjectResultExecutor.cs), the class that actually sends object results, I see it has code to [handle IAsyncEnumerable](https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Infrastructure/ObjectResultExecutor.cs#L129) – Panagiotis Kanavos Mar 10 '20 at 08:07
  • 2
    You can try returning `ActionResult`, eg: `return Ok(retrievedDtos)`. – Panagiotis Kanavos Mar 10 '20 at 08:09

1 Answers1

-1

this should work

    public ActionResult<IAsyncEnumerable<MyDto>> Get()
    {
        if(IsRequestInvalid())
        {
            // now can do.
            return Unauthorized();
        }

        return new ActionResult<IAsyncEnumerable<MyDto>>(DoSomeProcessing());

        IAsyncEnumerable<MyDto> DoSomeProcessing()
        {
            IAsyncEnumerable<MyDto> retrievedDtos = _someService.GetAllDtosAsync(_userId);

            await foreach(var currentDto in retrievedDtos)
            {
                //work with currentDto here

                yield return currentDto;
            }
        }
    }

if there is no processing of items before returning them better:

public ActionResult<IAsyncEnumerable<MyDto>> Get()
    {
        if(IsRequestInvalid())
        {
            // now can do
            return Unauthorized();
        }

        return new ActionResult<IAsyncEnumerable<MyDto>>(_someService.GetAllDtosAsync(_userId));
    }