Efficiently convert IQueryable<TSource> to ImmutableArray<TSource> using the async and await pattern in one step

Viewed 271

I'm trying to select a collection into an immutable array in the most efficient way possible.

I'm also want to make it clear the intent of the result set as readonly / immutable / not tracked.

var someCollection = await _dataContext
    .Set<someType>()
    .AsNoTracking()
    .ToArrayAsync();

I can do this then convert to immutable but that seems crazy.

var someImmutableCollection = someCollection
    .ToImmutableArray();

I can also just do this:

var someCollection = _dataContext
    .Set<someType>()
    .AsNoTracking()
    .ToImmutableArray();

But this is not async.

Is there a reason why there is no ToImmutableArrayAsync()?

Or is there a better way using async to get an immutable array from efcore?

1 Answers

I can do this then convert to immutable but that seems crazy.

var someImmutableCollection = someCollection
    .ToImmutableArray();

Crazy? Not so much. Let's have a look at the source code

public static async Task<TSource[]> ToArrayAsync<TSource>(
     [NotNull] this IQueryable<TSource> source,
     CancellationToken cancellationToken = default)
       => (await source.ToListAsync(cancellationToken).ConfigureAwait(false))
           .ToArray(); // anti-climax

Well, not much magic happening there.

Just create your own extension

public class MySuperExtensions
{   
    public static async Task<ImmutableArray<TSource>> ToImmutableArrayAsync<TSource>(
         [NotNull] this IQueryable<TSource> source,
         CancellationToken cancellationToken = default)
           => (await source.ToListAsync(cancellationToken).ConfigureAwait(false))
               .ToImmutableArray(); // yehaa
}

Crisis averted, problem solved.


Or more efficiently, saving an extra allocation and an IAsyncStateMachine Implementation

public static async Task<ImmutableArray<TSource>> ToImmutableArrayAsync<TSource>(
   [NotNull] this IQueryable<TSource> source,
   CancellationToken cancellationToken = default)
{
   var builder = ImmutableArray.CreateBuilder<TSource>();
   await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken))
       builder.Add(element);

   return builder.ToImmutable();
}
Related