Query Patterns
How-to · Applies to Darker V4
Query Pattern Introduction
This guide presents common query patterns you'll encounter when building real-world applications with Darker. While Queries and Query Objects covers the fundamentals of query design, and Implementing a Query Handler covers basic handler implementation, these pages focus on practical patterns for complex scenarios including pagination, projections, aggregations, and Entity Framework Core integration.
These patterns address real challenges like handling large data sets, optimizing query performance, working with related data, and implementing caching strategies. Each pattern includes complete, working examples that you can adapt to your specific needs.
Performance Best Practices
Pattern: Select Only What You Need
Always project to DTOs rather than loading full entities:
// ✅ Good: Select only needed fields
.Select(o => new OrderDto
{
Id = o.Id,
OrderDate = o.OrderDate,
CustomerName = o.Customer.Name
})
// ❌ Bad: Load entire entity
.Select(o => o) // or .ToList() directlyPattern: Avoid N+1 Queries
N+1 problem: Loading a collection, then querying related data for each item.
Pattern: Use Async All the Way
Always use async methods for I/O operations:
Real-World Example: Product Catalog Query
Here's a complete, production-ready example combining multiple patterns:
Usage in controller:
Best Practices Summary
Use pagination for any query that could return more than 100 items
Project to DTOs using
Select()- don't return domain entitiesAlways use
AsNoTracking()for read-only queriesUse
Include()wisely to avoid N+1 queries, but prefer projection when possibleCache appropriately - small, static lookup data is a good candidate
Handle nulls explicitly - use nullable reference types (
CustomerDto?)Use
CancellationToken- pass it through to all async operationsValidate query parameters in the query constructor
Use compiled queries for hot-path queries
Consider read replicas for scaling read-heavy workloads
Query Pattern Common Pitfalls
Loading entire collections without pagination - Always paginate large result sets
Forgetting
AsNoTracking()- Wastes memory and CPU for read-only queriesN+1 query problems - Use
Include()or projections to avoid multiple round tripsOver-fetching data - Select only the fields you need
Under-fetching (multiple queries) - Use joins/includes to get related data in one query
Not using
CancellationToken- Prevents graceful cancellation of long-running queriesReturning domain entities - Always project to DTOs for the query side
Caching too aggressively - Consider staleness tolerance and cache invalidation
Not optimizing database indexes - Ensure indexes exist for filter/sort columns
Ignoring query performance - Monitor slow queries and optimize hot paths
Further Reading
Parameterized Query Patterns - Lookups, filtered lists and multi-criteria search
Pagination Query Patterns - Offset-based and cursor-based paging
Projection Query Patterns - Returning only the fields a caller needs
Collection and Aggregation Query Patterns - Collections, counts and summary statistics
Entity Framework Core Query Integration - Tracking, eager loading and compiled queries
Implementing a Query Handler - Basic handler implementation patterns
Queries and Query Objects - Query design fundamentals
Query Pipeline - Decorators, logging, and resilience policies
Darker Basic Configuration - Getting started with Darker
CQRS with Brighter and Darker - Architectural patterns
Last updated
Was this helpful?
