DEV Community

Cover image for Boosting EF Core Performance with EF.CompileQuery
Morteza Jangjoo
Morteza Jangjoo

Posted on • Originally published at jangjoo.hashnode.dev

Boosting EF Core Performance with EF.CompileQuery

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); 
Enter fullscreen mode Exit fullscreen mode

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); 
Enter fullscreen mode Exit fullscreen mode

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"); 
Enter fullscreen mode Exit fullscreen mode

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 or EF.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)