9

I have AsyncPageable<T> and want to get only the first result from the list.

MS docs suggests using await foreach

// call a service method, which returns AsyncPageable<T>
AsyncPageable<SecretProperties> allSecretProperties = client.GetPropertiesOfSecretsAsync();

await foreach (SecretProperties secretProperties in allSecretProperties)
{
    Console.WriteLine(secretProperties.Name);
}

Is there any efficient way to get only the first result? Something like FirstOrDefaultAsync()?

At the moment I am using this code to get the first result.

var enumerator = response.Value.GetResultsAsync().GetAsyncEnumerator();
await enumerator.MoveNextAsync();
var result = enumerator.Current;
andy meissner
  • 1,202
  • 5
  • 15
Deivydas Voroneckis
  • 1,973
  • 3
  • 19
  • 40
  • 1
    It's not built specifically to accommodate this use case. I'd probably just have a non-conditional `break` in the `await foreach` rather than digging into the enumerator machinery. Also, of course, you still have to deal with 0 results. – Damien_The_Unbeliever Aug 25 '20 at 08:36
  • 1
    [Does this answer your question?](https://stackoverflow.com/a/58376703/12888024) – aepot Aug 25 '20 at 08:38
  • 2
    You are doing about as efficient as you will get. Just wrap it in an extension method. pulling a library in for this method is a waste – TheGeneral Aug 25 '20 at 09:13

2 Answers2

11

Since AsyncPageable<T> implements IAsyncEnumerable<T>, you can install System.Linq.Async nuget and use the methods it provides:

var result = await allSecretProperties.FirstOrDefaultAsync();
rytisk
  • 1,331
  • 2
  • 12
  • 19
  • `IAsyncEnumerable` does not contain `FirstOrDefaultAsync` so this doesn't work. – Deivydas Voroneckis Aug 25 '20 at 08:45
  • 3
    @DeivydasVoroneckis Did you install the NuGet? – rytisk Aug 25 '20 at 08:46
  • 3
    Beware: Using `System.Linq.Async` with EFCore caused my entire solution to break with Ambiguous references to all my linq `.where` clauses in files that import both `System.Linq` as well as `Microsoft.EntityFrameworkCore`. Known issue see: [The call is ambiguous between the following methods or properties (IAsyncEnumerable, IQueryable) #18220](https://github.com/dotnet/efcore/issues/18220) AND [System.Interactive.Async call is ambiguous error when IX-Async is referenced #18124](https://github.com/dotnet/efcore/issues/18124) – ttugates Mar 02 '21 at 14:17
  • 3
    @rytisk - It is a reasonable comment that can be expected to be relevant to a significant subset of the > 500 users having viewed this SO in the half year its been posted. Users using `AsyncPageable`, a MS Azure Library, that happen to also use MS EFCore can benefit from the free info in my comment. It is info I wish I knew before following the advice of this perfectly valid answer you posted. Re: "Have you read the OP's question?" - I am avail for chat should you wish to delve into conversation. – ttugates Mar 03 '21 at 15:56
6

Just wrap what you have in an extension method.

// hrmmm, im not sure
public static async Task<T> FirstOrDefaultAsync<T>(this IAsyncEnumerable<T> enumerable)
{
   await foreach (var item in enumerable)
      return item;
   return default;
}

// This is more efficient 
public static async Task<T> FirstOrDefaultAsync2<T>(this IAsyncEnumerable<T> enumerable)
{
   var enumerator = enumerable.GetAsyncEnumerator();
   await enumerator.MoveNextAsync();
   return enumerator.Current;
}

Check the IL to them both here

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • 2
    More efficient, partly because it's failing to respect async disposal. – Damien_The_Unbeliever Aug 25 '20 at 09:27
  • @Damien_The_Unbeliever Yeah i have been trying to work out if that is going to cause an issue. Also after looking at these 2 code blocks for a while, I think aesthetically I would go with the foreach myself – TheGeneral Aug 25 '20 at 09:32