Linq has two ways to execute a query:

  • Through enumerating the query (if it's a query which returns an IEnumerable)
  • Through calling a single-value returning method.

The enumeration of a query is always synchronous in .NET, as it uses foreach which will call GetEnumerator() on the IQueryable and this will execute the query and enumerate the results. As there's no await possible in foreach(), executing a linq query asynchronously this way is not possible through foreach.

We have solved this by implementing ToListAsync, ToArrayAsync and ExecuteAsync extension methods for IQueryable which execute the query asynchronously and then return the values obtained by the query as the result of the method. 

To get details about the various methods, please consult the LLBLGen Pro Runtime Library Reference Manual, which is an additional download from the LLBLGen Pro website.

All nested queries inside Linq queries as well as prefetch path definitions are executed asynchronously if the main query is executed asynchronously as well.

The Async methods available.

The following methods have been added to fetch linq queries asynchronously. They're all IQueryable extension methods. They follow the same pattern as with the QuerySpec methods added for Async programming: there are two overloads per method signature: one which accepts a CancellationToken and one which calls that overload and passes CancellationToken.None.

The naming scheme is equal to what's been used in QuerySpec as well: All methods below have the format SynchronousMethodEquivalentAsync. So ToArrayAsync is the asynchronous variant of ToArray, with the same behavior, input and output.

All async methods have to be the last method called on a query. You can't wrap them inside another call in a linq query, as the async methods have to be awaited. Example: (not the same query, but they illustrate the place of the method call)

// Wrong
var q = from c in metaData.Customer
    select new { CustomerId=c.CustomerId, Order = c.Orders.FirstAsync()};

// Right
var q = (from c in metaData.Customer
    select c).FirstAsync(c=>c.Country=="USA");

IEnumerable returning methods

The following methods have been added to execute an IEnumerable returning Linq query asynchronously. All methods have overloads which accept a CancellationToken.

  • ToArrayAsync<TSource>()
  • ToListAsync<TSource>()
  • ExecuteAsync(). Equal to IQueryable.Execute, the synchronous method to execute a query. Calls ILLBLGenProQuery.Execute.
  • ExecuteAsync<TResult>(). Returns the query result casted to TResult. Equal to IQueryable.Execute, the synchronous method to execute a Linq query. Calls ILLBLGenProQuery.Execute.

Scalar value returning methods

The following methods have been added to execute a scalar returning linq query asynchronously. The difference with the IEnumerable returning methods is that the method which defines what value to return is also the async method. This means that the async method is part of the linq query.

Async variants of methods unique to LLBLGen Pro:

  • CountColumnAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>)
  • CountColumnAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>, bool)
  • StandardDeviationAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>)
  • StandardDeviationAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>, bool)
  • VarianceAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>)
  • VarianceAsync<TSource, TColumnType>(Expression<Func<TSource, TColumnType>>, bool)

Async variants of methods provided by Queryable / System.Linq

  • AllAsync<TSource>(Expression<Func<TSource, bool>>)
  • AnyAsync<TSource>()
  • AnyAsync<TSource>(Expression<Func<TSource, bool>>)
  • AverageAsync(). Various overloads for different numeric types.
  • AverageAsync<TSource>(Expression<Func<TSource, returntype>>). Various overloads for different numeric types.
  • ContainsAsync<TSource>(TSource)
  • CountAsync<TSource>()
  • CountAsync<TSource>(Expression<Func<TSource, bool>>)
  • ElementAtAsync<TSource>(int)
  • ElementAtOrDefaultAsync<TSource>(int)
  • FirstAsync<TSource>()
  • FirstAsync<TSource>(Expression<Func<TSource, bool>>)
  • FirstOrDefaultAsync<TSource>()
  • FirstOrDefaultAsync<TSource>(Expression<Func<TSource, bool>>)
  • LongCountAsync<TSource>()
  • LongCountAsync<TSource>(Expression<Func<TSource, bool>>)
  • MaxAsync<TSource>()
  • MaxAsync<TSource, TResult>(Expression<Func<TSource, TResult>>)
  • MinAsync<TSource>()
  • MinAsync<TSource, TResult>(Expression<Func<TSource, TResult>>)
  • SingleAsync<TSource>()
  • SingleAsync<TSource>(Expression<Func<TSource, bool>>)
  • SingleOrDefaultAsync<TSource>()
  • SingleOrDefaultAsync<TSource>(Expression<Func<TSource, bool>>)
  • SumAsync(). Various overloads for different numeric types.
  • SumAsync<TSource>(Expression<Func<TSource, returntype>>). Various overloads for different numeric types.