CompileQuery in EF Core


Entity Framework Core (EF Core) is a powerful ORM (Object-Relational Mapper) for .NET developers. While it provides great flexibility and ease of use, sometimes performance becomes a concern — especially when executing the same LINQ queries repeatedly. One underutilized but powerful feature that addresses this issue is compiled queries using EF.CompileQuery
.
In this article, we'll explore how EF.CompileQuery
works, when to use it, and how it can help you improve performance in high-traffic or performance-critical applications.
What Is EF.CompileQuery
?
Every time you execute a LINQ query in EF Core, it gets parsed, translated, and compiled into SQL. This process involves several steps and can be relatively expensive, especially for complex queries.
EF.CompileQuery
allows you to precompile a query once and reuse it multiple times, avoiding the overhead of repeated query compilation.
Syntax
var compiledQuery = EF.CompileQuery(
(MyDbContext context, int id) =>
context.Users.FirstOrDefault(u => u.Id == id)
);
// Usage
var user = compiledQuery(dbContext, 1);
When Should You Use Compiled Queries?
Compiled queries are most effective when:
The same query is executed frequently with different parameters.
The query is performance-critical, and latency matters.
The query is relatively complex, so compilation overhead is non-trivial.
For example: fetching user profiles by ID, checking login credentials, retrieving config settings — these are common use-cases.
Limitations
While compiled queries are powerful, they come with a few limitations:
No async support with
EF.CompileQuery
(useEF.CompileAsyncQuery
instead).Less flexible than dynamic LINQ queries.
You cannot use them for queries that change structure dynamically.
Async Variant: EF.CompileAsyncQuery
EF Core also supports asynchronous compiled queries:
var compiledAsyncQuery = EF.CompileAsyncQuery(
(MyDbContext context, int id) =>
context.Users.FirstOrDefault(u => u.Id == id)
);
// Usage
var user = await compiledAsyncQuery(dbContext, 1);
This is ideal in ASP.NET Core applications where asynchronous I/O is essential for scalability.
Benchmark: Is It Really Faster?
Yes, but it depends on the scenario.
Scenario | Normal Query | Compiled Query |
First run | High latency (compilation) | Low (precompiled) |
Repeated runs | Medium latency | Very low latency |
In microbenchmarks, compiled queries can save up to 30-40% on CPU for hot paths.
Best Practices
Cache compiled queries as static fields.
Use meaningful parameterization.
Avoid overusing it; not every query benefits from compilation.
Profile before and after to verify impact.
Real-World Example
public static class CompiledQueries
{
public static readonly Func<MyDbContext, string, User?> GetUserByUsername =
EF.CompileQuery((MyDbContext context, string username) =>
context.Users.FirstOrDefault(u => u.Username == username));
}
// Usage
var user = CompiledQueries.GetUserByUsername(context, "morteza");
This pattern is great for queries you call hundreds or thousands of times in an app's lifecycle.
Summary
Compiled queries in EF Core are a powerful optimization for high-performance applications. While they’re not necessary for every situation, using them for hot-path, frequently-used queries can significantly reduce overhead.
TL;DR
Use
EF.CompileQuery
for repeated LINQ queries.Save CPU and reduce latency.
Use
EF.CompileAsyncQuery
for async code.Cache the compiled delegates and reuse them.
💬 If you found this helpful, feel free to share or drop your thoughts in the comments!
I’m Morteza Jangjoo and “Explaining things I wish someone had explained to me”
Subscribe to my newsletter
Read articles from Morteza Jangjoo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
