I am able to compile
public async Task<IEnumerable<T>> GetResultsFromQueryExecutionId<T>(string queryExecutionId)
{
await using var csvResponseStream = await transferUtility.OpenStreamAsync("bucket", "blah.csv");
return GetResultsFromResponseStream<T>(csvResponseStream);
}
private IEnumerable<T> GetResultsFromResponseStream<T>(Stream csvResponseStream)
{
using var streamReader = new StreamReader(csvResponseStream);
using var csvReader = new CsvReader(streamReader, csvConfiguration);
foreach (var record in csvReader.GetRecords<T>())
{
yield return record;
}
}
but if I try to remove the private method and run the code in one method I get a compile error "The body of 'GetResultsFromQueryExecutionId' cannot be an iterator block because 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable>' is not an async iterator interface type"
I tried
public async Task<IEnumerable<T>> GetResultsFromQueryExecutionId<T>(string queryExecutionId, string logPrefix = null)
{
await using var csvResponseStream = await transferUtility.OpenStreamAsync("bucket", "blah.csv");
using var streamReader = new StreamReader(csvResponseStream);
using var csvReader = new CsvReader(streamReader, csvConfiguration);
foreach (var record in csvReader.GetRecords<T>())
{
yield return record;
}
}
I was expecting the code to be equivalent. Is anyone able to explain why the compiler will not allow me to combine this into one method please?
yield return
is a very special construct that forces the method it's in to get compiled in a completely different mode. You can only return IAsyncEnumerable<>
or IEnumerable<>
(or types with similar methods on them) from methods that use it, so if you want to return something different you'll need to break the code into separate methods as you have.
That said, you may want to look at a couple of other options. You can use a Local Function that's nested inside of your outer function, to help keep things grouped together.
public async Task<IEnumerable<T>> GetResultsFromQueryExecutionId<T>(string queryExecutionId)
{
await Task.Yield();
return GetResultsFromResponseStream();
IEnumerable<T> GetResultsFromResponseStream()
{
foreach (var record in new T[0] )
{
yield return record;
}
}
}
It might be appropriate to return an IAsyncEnumerable<T>
instead:
public async IAsyncEnumerable<T> GetResultsFromQueryExecutionId<T>(string queryExecutionId)
{
await Task.Yield();
foreach (var record in new T[0] )
{
yield return record;
}
}
This would change the way people use your method.
// Before
var results = await GetResultsFromQueryExecutionId<int>("foo");
foreach(var result in results)
{
...
}
// After
var results = GetResultsFromQueryExecutionId<int>("foo");
await foreach (var result in results)
{
...
}