Run enumeration of IAsyncEnumerable
twice not possible?
Once CountAsync
has been run, the await foreach
won't enumerate any item. Why?
It seems there is no Reset method on the AsyncEnumerator
.
var count = await itemsToImport.CountAsync();
await foreach (var importEntity in itemsToImport)
{
// won't run
}
Source of data:
private IAsyncEnumerable<TEntity> InternalImportFromStream(TextReader reader)
{
var csvReader = new CsvReader(reader, Config);
return csvReader.GetRecordsAsync<TEntity>();
}
This has nothing to do with resetting an IAsyncEnumerator. This code attempts to generate a second IAsyncEnumerator, which, just like with IEnumerable.GetEnumerator() is only possible on some kinds of collections. If the Enumerable (async or not) is an abstraction over some sort of forward-only data structure, then GetEnumerator/GetAsyncEnumerator will fail.
And even when it doesn't fail, it's sometimes expensive. For instance it might run a database query or hit an remote API each time it's enumerated. This is why IEnumerable/IAsyncEnumerable make poor public return types from functions, as they fail to describe the capabilities of the returned collection, and almost the only thing you can do with the value is materialize it with .ToList/ToListAsync.
Eg, this works fine:
static async IAsyncEnumerable<int> Col()
{
for (int i = 1; i <= 10; i++)
{
yield return i;
}
}
static void Main(string[] args)
{
Run().Wait();
}
static async Task Run()
{
var col = Col();
var count = await col.CountAsync();
await foreach (var dataPoint in col)
{
Console.WriteLine(dataPoint);
}
}
It seems to be impossible to reset an IAsyncEnumerable by the interface itself, due to the fact, that there is no Reset method on the IAsyncEnumerator interface.
In this specific example the second enumeration won't work because the IAsyncEnumerable targets to a Stream. Once the stream has been read, the position cursor targets the stream's end. if you have control over the stream or a reference to it, (which I don't) you could set the position to 0 again and enumerate it again.
I tend to use ToListAsync and then get count out of its Count property and iterate the items synchronously because they are already loaded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With