Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Where never talks to the database, so there is nothing for it to await. It only adds a predicate to the query expression that an IQueryable<T> carries. ToListAsync exists because it is the step that executes the composed query, asynchronously pulls the rows back, and returns them as a List<T>. The working rule in EF Core is simple: compose the query first, and execute it at a terminal operation.
Why there is no WhereAsync
An asynchronous method earns its place where a program waits on I/O. Where does not wait on anything. When you call it on an EF Core IQueryable<T>, the operator records a predicate inside an expression tree and returns a new queryable. That tree can keep growing with OrderBy, Select, and other operators, and EF Core’s provider translates the finished tree into the database’s query language.
As an Amazon Associate I earn from qualifying purchases.
Microsoft Learn’s EF Core asynchronous programming guidance states the design directly:
Recommended Free Tools
“Note that there are no async versions of some LINQ operators such as Where or OrderBy, because these only build up the LINQ expression tree and don’t cause the query to be executed in the database.”
#1 Best Overall
This is a documentation statement from Microsoft Learn, not a quotation from a named engineer. The same logic explains why EF Core’s async surface is built around operations such as ToListAsync, FirstOrDefaultAsync, CountAsync, and SaveChangesAsync: each of those is the point where the database is actually contacted.
Do not carry this reasoning over to in-memory LINQ. Over an IEnumerable<T>, Where runs its predicate as the sequence is enumerated, and there is no database round trip to await. The question here concerns EF Core’s provider-backed query path, where the same operator is only a description of work.
Rank #2
What ToListAsync does
ToListAsync is a terminal operation. It is an extension method on IQueryable<TSource>, defined in the Microsoft.EntityFrameworkCore namespace, and its shape is Task<List<TSource>>. It accepts an optional CancellationToken. The EF Core 10.0 API reference describes it as asynchronously creating a List<T> from an IQueryable<T> by enumerating it asynchronously.
Building a list in memory is cheap. What costs time is fetching the rows, and that fetch is I/O. Calling ToListAsync lets the calling code await the database instead of blocking a thread while the results arrive. The synchronous counterpart, ToList, performs the same enumeration on the calling thread.
When the query actually runs
Execution happens when results are consumed, not when a LINQ operator is called. In practice, consumption means enumeration or one of the terminal operators such as ToList, Single, or Count, or their async counterparts. The sequence looks like this:
- Start from the
DbSet, for examplecontext.Blogs. No query has been sent yet. - Add translatable filters and projections with
Where,OrderBy, andSelect. Each call returns a newIQueryable<T>and still sends nothing. - Call a terminal operation such as
ToListAsync. EF Core translates the expression tree, sends the command, and awaits the result. - Use the returned
List<T>. The rows are now in application memory, and further LINQ over the list is ordinary in-memory LINQ.
A representative query looks like this:
var blogs = await context.Blogs
.Where(b => b.Rating > 3)
.ToListAsync();
Because the predicate is part of the query, the database filters the rows. Only the matching blogs cross the network.
Rank #4
ToListAsync or AsAsyncEnumerable
Both operations execute the query asynchronously. They differ in how much data sits in memory at once, and that difference should drive the choice.
| Aspect | ToListAsync | AsAsyncEnumerable with await foreach |
|---|---|---|
| Return type | Task<List<T>> |
IAsyncEnumerable<T> |
| Result handling | Buffers the full result set in memory | Streams rows one at a time |
| Memory growth | Grows with the total number of results | Does not need to hold the whole result set |
| Best fit | Small results, results that must be a list, or results enumerated more than once | Large result sets processed row by row |
| Client-side LINQ | Ordinary LINQ over the materialized list | Operators over IAsyncEnumerable<T>, which depend on the target framework (see below) |
Microsoft’s efficient-querying guidance points out that buffering is not free. For a large result, prefer streaming, and also consider limiting the number of rows or paging through them. Avoid calling ToListAsync only to run a single extra operation that could have been composed into the query or applied to a stream.
Best Value
Filtering on the server before the execution boundary
Put every translatable predicate and projection before AsAsyncEnumerable. Once rows have left the database, a filter written in C# runs against data that has already been transferred. For logic that EF Core cannot translate, the usual pattern is to compose the translatable part on the query, cross the boundary with AsAsyncEnumerable, and then apply the local .NET logic to the streamed rows:
await foreach (var blog in context.Blogs
.Where(b => b.Rating > 3)
.AsAsyncEnumerable())
{
if (IsEligible(blog)) // local .NET method, not translated to SQL
{
Process(blog);
}
}
EF Core’s client-evaluation guidance says that untranslatable expressions in the top-level projection can be evaluated on the client, and that other client-side work should be made explicit with AsAsyncEnumerable or ToListAsync. Making the boundary explicit keeps the database-bound and client-bound parts easy to read.
Operators over IAsyncEnumerable<T> are a separate question. Microsoft Learn notes that these LINQ operators are introduced in .NET 10. On earlier target frameworks, the guidance points to the System.Linq.Async package. Do not assume that every ordinary Where is available on an IAsyncEnumerable<T> in every framework; check the target before writing the pipeline.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteCaveats that break async EF Core code
- Overlapping operations on one context. EF Core does not support multiple parallel operations on a single
DbContext. Await each async query before starting the next operation on the same context. - Cancellation depends on the provider. The token passed to
ToListAsyncis observed while the task is awaited, and EF Core forwards it to the underlying provider. The provider may or may not honor it, so do not treat cancellation as an immediate database stop. - Provider-specific async issues. EF Core’s async documentation notes known issues in the async implementation of Microsoft.Data.SqlClient. If you see unexpected performance problems, especially with large text or binary values, the documentation advises investigating synchronous command execution as a comparison. This is a provider-level caveat, not a reason to avoid async EF Core.
- Accidental early execution. Calling
ToListAsyncbefore a filter that could have been part of the query sends more data than necessary. Check where the list is created in the pipeline.
Practical reading of the pattern
Read a query chain from the bottom up. The terminal call is the only point where a database command is sent. Everything above it is a description of that command, which is why Where has no async version and ToListAsync does.
Quick Recap
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




