How to validate arguments for IAsyncEnumerable returning method before actual iteration takes place?

Viewed 186

I have a simple scenario where I have a class with the following method:

public async IAsyncEnumerable<Entity> GetEntities(IQueryOptions options){
  if(!validator.ValidateQuery(options)) { throw new ArgumentException(nameof(options));}

  var data = dataSource.ReadEntitiesAsync(options);

  await foreach (var entity in data) { yield return await converter.ConvertAsync(entity);}
}

Is it possible to have the ArgumentException thrown exactly at the GetEntities() method call, and not after first step of iteration like here:

await foreach(var e in GetEntities(options)) { // some code here }

I'm asking because when I want to return IAsyncEnumerable up to my API controller the exception actually is thrown in the framework code. I have no chance to catch it, and return a HTTP 404 BAD REQUEST code. Surely I can intercept exceptions in the request pipeline, but sometimes I want to wrap them in other exceptions depending on the abstraction layer they come from.

1 Answers

Split it into two functions. Here's an example:

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
                    
public class Program
{
    public static async Task Main()
    {
        var enumeration = AsyncEnumeration(-1);
        await foreach(int x in enumeration)
        {
            Console.WriteLine(x);
        }
    }
    
    public static IAsyncEnumerable<int> AsyncEnumeration(int count)
    {
        if (count < 1)
            throw new ArgumentOutOfRangeException();
        
        return AsyncEnumerationCore(count);
    }
    
    private static async IAsyncEnumerable<int> AsyncEnumerationCore(int count)
    {
        for (int i = 0; i < count; i++)
        {
            yield return i;
            await Task.Delay(1);
        }
    }
}
Related