Entity Framework Core (EF Core) is a robust ORM for .NET developers. While it provides a high-level abstraction over relational databases, some scenarios require performance optimizations — especially when executing the same queries repeatedly.
One powerful, but often overlooked feature is EF.CompileQuery
, which allows you to precompile LINQ queries and reuse them efficiently.
What Is EF.CompileQuery?
When EF Core executes a LINQ query, it needs to parse, translate, and compile it into SQL. This process happens every time, even if the query structure stays the same.
To avoid this overhead, EF Core provides the EF.CompileQuery
method which lets you compile a query once and reuse it.
Example
var compiledQuery = EF.CompileQuery( (MyDbContext context, int id) => context.Users.FirstOrDefault(u => u.Id == id) ); // Use it like this: var user = compiledQuery(dbContext, 1);
This significantly improves performance, especially in high-traffic apps.
When to Use Compiled Queries
Use compiled queries when:
- You run the same LINQ query many times with different parameters.
- The query is performance-critical.
- The query is complex enough that compilation takes time.
Typical use cases include:
- Getting a user by ID
- Fetching application config
- Repeated queries in APIs or background services
Limitations
Compiled queries are not a silver bullet. Be aware of:
- No async support with
CompileQuery
(but see below). - Less flexibility — can't use for dynamic query structures.
- Slightly more boilerplate — you manage delegates manually.
Async Variant: EF.CompileAsyncQuery
EF Core also provides EF.CompileAsyncQuery
for async query execution:
var compiledAsyncQuery = EF.CompileAsyncQuery( (MyDbContext context, int id) => context.Users.FirstOrDefault(u => u.Id == id) ); var user = await compiledAsyncQuery(dbContext, 1);
This is perfect for ASP.NET Core apps where async I/O boosts scalability.
Benchmark Comparison
Run Type | Regular Query | Compiled Query |
---|---|---|
First run | High overhead | Precompiled |
Repeated runs | Moderate | Very fast |
In performance-critical paths, compiled queries can reduce overhead by 30–40%.
Best Practices
- Store compiled queries in static fields or singletons.
- Use meaningful parameters.
- Profile before and after introducing compiled queries.
- Don't over-optimize — apply only to hot paths.
Real-World Usage
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 fits well in layered architectures and services.
Conclusion
Compiled queries can be a game-changer for EF Core apps that need to squeeze out more performance. By avoiding repeated query compilation, you save valuable CPU cycles and reduce latency.
TL;DR
- Use
EF.CompileQuery
orEF.CompileAsyncQuery
to avoid repeated compilation. - Great for performance-critical or frequently-used queries.
- Use wisely and measure performance impact.
This post is also available on my Hashnode blog with additional insights.
I’m Morteza Jangjoo and “Explaining things I wish someone had explained to me”
Top comments (0)