L'optimisation des performances des requêtes Entity Framework Core est un enjeu crucial pour tout développeur .NET cherchant à construire des applications performantes et scalables. Dans cet article, nous explorerons les techniques essentielles pour optimiser vos requêtes EF Core et améliorer significativement les performances de vos applications.
Concepts fondamentaux de l'optimisation EF Core
Avant de plonger dans les techniques d'optimisation, il est important de comprendre comment EF Core traduit nos requêtes LINQ en requêtes SQL et les exécute.
Le cycle de vie d'une requête EF Core
- Création de l'expression LINQ
- Traduction en SQL par le provider
- Exécution de la requête
- Matérialisation des résultats
Techniques d'optimisation essentielles
1. Chargement sélectif avec Select
// ❌ Mauvaise pratique : chargement de toutes les colonnes
var users = await context.Users.ToListAsync();
// ✅ Bonne pratique : chargement sélectif
var userDtos = await context.Users
.Select(u => new UserDto
{
Id = u.Id,
Name = u.Name,
Email = u.Email
})
.ToListAsync();
2. Utilisation efficace des includes
// ❌ Chargement excessif
var orders = await context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Product)
.ToListAsync();
// ✅ Chargement optimisé avec filtrage
var orders = await context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems.Where(oi => oi.Quantity > 0))
.ThenInclude(oi => oi.Product)
.Where(o => o.OrderDate >= DateTime.Today.AddDays(-30))
.ToListAsync();
3. Pagination efficace
public async Task> GetPagedResultAsync(
IQueryable query,
int pageNumber,
int pageSize)
{
return await query
.Skip((pageNumber - 1) pageSize)
.Take(pageSize)
.ToListAsync();
}
Bonnes pratiques et patterns
1. Utilisation du pattern Repository
public interface IRepository where T : class
{
Task GetByIdAsync(int id);
Task> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
public class Repository : IRepository where T : class
{
private readonly DbContext _context;
private readonly DbSet _dbSet;
public Repository(DbContext context)
{
_context = context;
_dbSet = context.Set();
}
public async Task GetByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
// Autres implémentations...
}
2. Mise en cache intelligente
public class CachedRepository : IRepository where T : class
{
private readonly IRepository _repository;
private readonly IMemoryCache _cache;
private readonly string _cacheKey;
public CachedRepository(
IRepository repository,
IMemoryCache cache,
string cacheKey)
{
_repository = repository;
_cache = cache;
_cacheKey = cacheKey;
}
public async Task GetByIdAsync(int id)
{
return await _cache.GetOrCreateAsync(
$"{_cacheKey}_{id}",
async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return await _repository.GetByIdAsync(id);
});
}
}
Optimisation des performances
1. Tracking des entités
// ❌ Avec tracking (par défaut)
var users = await context.Users.ToListAsync();
// ✅ Sans tracking pour les requêtes en lecture seule
var users = await context.Users
.AsNoTracking()
.ToListAsync();
2. Compilation des requêtes
private static readonly Func> GetCustomerById =
EF.CompileAsyncQuery((ApplicationDbContext context, int id) =>
context.Customers
.Include(c => c.Orders)
.FirstOrDefault(c => c.Id == id));
// Utilisation
var customer = await GetCustomerById(context, 1);
Tests et validation
public class QueryOptimizationTests
{
[Fact]
public async Task GetCustomers_WithPagination_ReturnsCorrectPage()
{
// Arrange
using var context = new TestDbContext();
var repository = new CustomerRepository(context);
// Act
var customers = await repository.GetPagedResultAsync(1, 10);
// Assert
Assert.Equal(10, customers.Count());
}
}
Considérations de performance
- Utilisez SQL Server Profiler pour analyser les requêtes générées
- Activez le logging des requêtes en développement
- Surveillez les performances avec Application Insights
- Implémentez des health checks pour monitorer la base de données
Conclusion
L'optimisation des requêtes EF Core est un aspect crucial du développement .NET moderne. En appliquant les techniques présentées dans cet article, vous pourrez significativement améliorer les performances de vos applications. N'oubliez pas de toujours mesurer l'impact de vos optimisations et de les adapter aux besoins spécifiques de votre projet.
Ressources complémentaires :
- Documentation officielle EF Core
- GitHub EF Core
- Blogs de la communauté .NET