3

I have an async method that returns an IAsyncEnumerable using yield. In some cases, it may be needed to get the items synchronously so I want to make another method for this that returns IEnumerable by running the existing method synchronously in order to avoid duplicate code.

async IAsyncEnumerable<Foo> GetItemsAsync()
{
    yield return ...
}

IEnumerable<Foo> GetItems() => GetItemsAsync() // Run shyncronously

I read plenty solutions about running async methods synchronously using techniques like the Task.RunSynchronously or simply calling it in a sync context and ignoring the warning. However, it seems neither of these is possible in the case of async enumerables. The interface features no RunSynchronously method and trying to enumerate through it using a foreach that is not awaited causes a compilation error. Getting the enumerator and running MoveNext synchronously also doesn't seem to be an option since that method returns ValueTask which doesn't feature Wait methods and I need to capture the result of the task to know when to stop iterating.

So is it possible to force my async enomerator method synchronously and get the result as IEnumerable, and if so, how?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
TheBoxyBear
  • 371
  • 2
  • 15
  • This is a specific case of the more general question "how do I call asynchronous code from synchronous code?" The best answer is *you don't*, but if you *have* to, there are [various hacks available](https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development). – Stephen Cleary Dec 22 '21 at 00:05

1 Answers1

2

You could use the ToEnumerable extension method from the System.Linq.Async package. The signature of this method is:

public static IEnumerable<TSource> ToEnumerable<TSource>(
    this IAsyncEnumerable<TSource> source)

...and you could use it like this:

IEnumerable<Foo> GetItems() => GetItemsAsync().ToEnumerable();

Update: an extension method ToBlockingEnumerable with identical functionality might be included in the next version of the .NET platform (.NET 7). Related GitHub issue: Add an IAsyncEnumerable.ToEnumerable extension method.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • Thank you. However it seems this is not available for .net 5. I have been considering switching to .net 6 for a while and I might justh have to in order to use this. Just a heads up for anyone using .net 5. – TheBoxyBear Dec 22 '21 at 00:24
  • @TheBoxyBear AFAIKS the latest version of the System.Linq.Async package (5.1.0) is supported on the .NET Core 3.1 and later. Which includes .NET 5. – Theodor Zoulias Dec 22 '21 at 01:03
  • 1
    Turns out it is supported in both .NET versions. The reason it said it wasn't for me is because I had left out System.Linq from the using statements but .NET 6 adds it implicitly. – TheBoxyBear Dec 23 '21 at 05:38