EF Core query habits
On trails:EFCOREstep 6/8
Optimizing data access is crucial for application performance.
- Select only the data you need. Use
Select()projections to retrieve only the properties you need, instead of fetching entire entities. - Filter early. Apply
Where()clauses as early as possible in the query, so the filtering happens on the database server and less data is transferred. This is theIQueryable<T>pointLINQ providers: IQueryable and IEnumerableWhere a LINQ query actually runs depends on its provider, and on IQueryable versus IEnumerable.Opens in a new tab in practice. - Use
AsNoTracking()for read-only queries. If entities are retrieved for read-only purposes and won’t be modified and saved back, useAsNoTracking()on the query. It tells EF Core not to track changes for those entities, which can significantly reduce memory overhead and processing time. See change trackingChange tracking and SaveChangesHow EF Core notices what you changed, and turns it into SQL.Opens in a new tab. - Avoid the N+1 query problem. Be careful with lazy loading, or with inefficient loading of related data, which can lead to many small queries instead of a more efficient single query or a few. Use eager loading (
Include) or explicit loading strategically. See loading related dataLoading related dataEager, explicit and lazy loading, and the N+1 problem that lazy loading can cause.Opens in a new tab. - Use query splitting (
AsSplitQuery()). When a LINQ query loads multiple related collections, for example a blog and its posts and its tags, EF Core might generate a single SQL query that causes a “cartesian explosion” (many redundant rows).AsSplitQuery()tells EF Core to generate separate SQL queries for each included collection. That can be more efficient in such cases, by avoiding redundant data transfer. - Use global query filters. They let you define LINQ query predicates in
OnModelCreatingthat are applied automatically to all queries for a given entity type. That’s useful for features like soft deletes or multi-tenancy, done transparently.
C#
modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);Sources
- Advanced Performance Topics, EF Core, Microsoft Learn · accessed May 16, 2025