Entity Framework: Advanced Concepts
Welcome to the advanced topics section for Entity Framework. This guide will delve into more complex features and strategies to help you leverage the full power of EF in your .NET applications.
Querying Data Efficiently
Lazy Loading vs. Eager Loading
Understanding how related entities are loaded is crucial for performance. Lazy loading fetches related data only when it's accessed, while eager loading fetches it along with the primary entity. EF Core provides mechanisms for both.
- Lazy Loading: Enabled by default with navigation properties. Requires virtual navigation properties and the
Microsoft.EntityFrameworkCore.Proxies
package. - Eager Loading: Use the
.Include()
and.ThenInclude()
extension methods to specify related data to be loaded upfront.
// Eager Loading Example
var customers = _context.Customers
.Include(c => c.Orders)
.ThenInclude(o => o.OrderItems)
.ToList();
Projections and Select
Instead of retrieving entire entities, use projections to select only the specific data you need. This significantly reduces the amount of data transferred from the database and improves performance.
var customerOrderSummaries = _context.Customers
.Select(c => new {
CustomerName = c.Name,
OrderCount = c.Orders.Count(),
LastOrderDate = c.Orders.OrderByDescending(o => o.OrderDate).FirstOrDefault().OrderDate
})
.ToList();
Change Tracking and Concurrency
Advanced Change Tracking
EF Core's change tracker is a powerful feature that monitors changes to entities. You can manually control its behavior, detach entities, or attach existing ones.
_context.Entry(entity).State = EntityState.Modified;
_context.Attach(entity);
_context.Detach(entity);
Concurrency Control
Handling concurrent updates from multiple users or processes is vital. EF Core supports optimistic concurrency using row versioning or timestamp columns.
[Timestamp]
attribute (for EF Core) or IsRowVersion()
configuration.
// Example of configuring concurrency token
modelBuilder.Entity<Product>()
.Property<byte[]>("Timestamp")
.IsRowVersion();
Working with Raw SQL and Stored Procedures
Executing Raw SQL Queries
Sometimes, you might need to execute raw SQL queries for performance optimizations or complex operations not easily expressible in LINQ.
var products = _context.Products.FromSqlRaw("SELECT * FROM Products WHERE Category = {0}", "Electronics");
Calling Stored Procedures
EF Core allows you to call stored procedures and map their results to your entities or DTOs.
var customerOrders = _context.CustomerOrders.FromSqlInterpolated($"EXEC GetCustomerOrders {customerId}");
Performance Tuning Strategies
Batching Operations
For scenarios involving many inserts, updates, or deletes, consider using batching to reduce the number of round trips to the database.
Connection Resiliency and Retry Logic
Implement strategies to handle transient database connection failures gracefully.
Caching Strategies
For frequently accessed read-only data, integrate caching solutions (e.g., Redis, MemoryCache) to further improve performance.
Advanced Mapping Scenarios
Table-Per-Type (TPT) and Table-Per-Hierarchy (TPH)
Explore different inheritance mapping strategies for your domain models.
Value Objects and Owned Entities
Learn how to model complex types that don't have their own identity and are owned by another entity.
Complex Types and Shadow Properties
Understand how to map complex data structures and use shadow properties for properties that are not explicitly defined on your entity classes.
Extending Entity Framework
Custom Conventions
Define custom conventions to automate the configuration of your model.
Custom Value Converters
Map custom .NET types to database types.