109

So in C#8 we got the addition of the IAsyncEnumerable interface.

If we have a normal IEnumerable we can make a List or pretty much any other collection we want out of it. Thanks to Linq there.

var range = Enumerable.Range(0, 100);
var list = range.ToList();

Well now I want to convert my IAsyncEnumerable to a List and this of course asynchronously. Are there already Linq implementations for that case? If there isn't, how could I convert it myself then?

Twenty
  • 5,234
  • 4
  • 32
  • 67

2 Answers2

176

Sure - you just need the ToListAsync() method, which is in the System.Linq.Async NuGet package. Here's a complete example:

Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Linq.Async" Version="4.0.0" />
  </ItemGroup>

</Project>

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        IAsyncEnumerable<string> sequence = GetStringsAsync();
        List<string> list = await sequence.ToListAsync();
        Console.WriteLine(list.Count);
    }

    static async IAsyncEnumerable<string> GetStringsAsync()
    {
        yield return "first";
        await Task.Delay(1000);
        yield return "second";
        await Task.Delay(1000);
        yield return "third";
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    Thanks for that one, interesting that it isn't part of the .Net Core 3.1 Framework yet. – Twenty Dec 17 '19 at 19:07
  • 6
    @Twenty: As a separate package, it's easily usable on platforms stretching way back. – Jon Skeet Dec 17 '19 at 19:20
  • 2
    @ca9163d9: "It gets the error" doesn't really provide much information. I suggest you ask a new question with a complete [mcve]. – Jon Skeet Feb 12 '20 at 22:08
  • @JonSkeet, reproduced it just now. It failed `await iOrderedQueryable.ToListAsync(cancellationToken)` after added the nuget package. The error is gone after I removed the nuget package. – ca9163d9 Apr 11 '20 at 03:56
  • The code `await iOrderedQueryable.ToListAsync(cancellationToken)` is pre-existed code. (.Net core 3.1) – ca9163d9 Apr 11 '20 at 04:03
  • 2
    @ca9163d9: As I said before, please ask a new question with a complete example - and please make the error clearer than "it failed". – Jon Skeet Apr 11 '20 at 05:46
  • @JonSkeet is System.Linq.Async compatible with .net 5? I haven't had much success getting a project I want to use it with to recognize it. – db2 Jun 14 '21 at 17:09
  • 1
    @db2: Yes, it is - you'd need to be more specific about the problems for me to be able to help. I suggest you ask a new question with a [mcve]. – Jon Skeet Jun 14 '21 at 18:01
  • @JonSkeet thanks, I'll try and get it sorted out, it's probably just a silly vs code problem. – db2 Jun 14 '21 at 19:04
  • 1
    @JonSkeet I am pretty sure they are using EF Core and the call is now ambiguous with EFCore's `.ToListAsync()`. – El Mac Jun 17 '21 at 11:54
  • 1
    @ElMac: Quite possibly. There are all kinds of reasons why it could be going wrong, but without knowing anything about the code or the symptoms, there's not a lot more that can be said. – Jon Skeet Jun 17 '21 at 12:02
23

On the off chance that you don't want to to bring in a NuGet package, here is (probably something similar to) the extension method mentioned in the package:

public static class AsyncEnumerableExtensions
{
    public static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items,
        CancellationToken cancellationToken = default)
    {
        var results = new List<T>();
        await foreach (var item in items.WithCancellation(cancellationToken)
                                        .ConfigureAwait(false))
            results.Add(item);
        return results;
    }
}
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Jamie Twells
  • 1,924
  • 4
  • 26
  • 56
  • 2
    https://github.com/dotnet/reactive/blob/7fe854ff18e8bbf2548bc18cd1dd5019a923e34a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToList.cs#L32 – Ian Kemp Nov 02 '20 at 11:17
  • Just curious - does anyone know why the source linked by Ian Kemp defines the main implementation in a static local function 'Core' and then calls it directly, instead of just implementing it inline? – Andrew Williamson Jan 10 '21 at 20:59
  • 4
    @Andrew Williamson that's been changed in a recent commit. The reason will be for performance. It now does some validation of the parameters. You'll notice the outer function isn't marked async, only the local function is. So now if the validation fails we will not have gone to all the trouble of setting up the async state machine. – Jamie Twells Jan 12 '21 at 07:20
  • Why wouldn't you want to use the nuget package? It's not one of those "shady" packages but one supported by the dotnetfoundation. – Jonas Stensved Apr 29 '21 at 11:51
  • 6
    @JonasStensved because it may cause clashes, if it's used together with EF Core for example. I use the `ToListAsync` just once in my code until now, and if I add the Nuget package, I get a lot of ambiguous errors because EF Core is a dependency in the same project. – El Mac Jun 17 '21 at 11:56
  • Note that this *enforces the tasks are run in order*. (The first is awaiting before moving onto the 2nd). That may be what you want, but it could also cause deadlocks (if you expect them to be in Parallel like Task.WhenAll, and the 2nd task has a dependency on the first). – Mike S Mar 27 '22 at 19:28
  • This is also useful for my case where I am required to convert from an `AsyncEnumerable` to a `Task>` with a cancellation token at my disposal. `Task>` is required for interfacing reasons. – alelom Mar 23 '23 at 19:59