Les filtres de requêtes nommés
Présentation
En 2017, les filtres globaux sont apparus dans Entity Framework Core 2 afin d'appliquer automatiquement des conditions à toutes les requêtes effectuées sur une entité. Ils sont particulièrement utiles pour implémenter des mécanismes tels que le soft delete, consistant à marquer les données supprimées sans les supprimer physiquement de la base, ou encore le multi-tenant, qui permet d'isoler les données pour chaque "client".
Jusqu'à présent, lorsqu'une entité nécessitait plusieurs filtres, il était nécessaire de les combiner dans une seule expression. Cette approche présentait une limitation importante : la désactivation des filtres à l'aide de la méthode IgnoreQueryFilters() supprimait l'ensemble des restrictions, sans possibilité d'en conserver certaines. Dans une application multi-tenant, cela pouvait conduire à des erreurs ou pire, l'exposition de données d’autres clients.
La version 10 d’EF Core introduit les filtres de requête nommés. Cette nouvelle fonctionnalité permet de définir plusieurs filtres indépendants sur une même entité, chacun étant identifié par un nom. Il devient alors possible de désactiver certains filtres tout en conservant les autres (car ils sont nommés). Cette évolution facilite sa maintenance et renforce la sécurité des applications, manipulant des données sensibles et/ou cloisonnées.
Exemple concret avec Entity Framework Core 9 (version précédente)
Voici une classe permettant de manipuler des projets. Chaque projet a un identifiant, un nom et est rattaché à un client (tenant) et un indicateur de suppression soft (suppression logique indiquant le projet comme étant supprimé, sans qu’il soit supprimé physiquement dans la table) :
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
// Multi-tenant
public string TenantId { get; set; }
// Soft delete
public bool IsDeleted { get; set; }
public Project(int id, string name, string tenantId, bool isDeleted)
{
Id = id;
Name = name;
TenantId = tenantId;
IsDeleted = isDeleted;
}
}
Avant Entity Framework Core 10, lorsque nous souhaitions ajouter un filtre permanent sur l’entité Project, afin qu’à chaque fois qu’EF Core interroge la table des projets, il ajoute automatiquement une condition pour ne récupérer que les projets non supprimés, et appartenant au tenant courant :
Ainsi, en écrivant :
EF Core exécute la requête SQL suivante :
Le filtre de requête défini sur l’entité Project est atomique. Il n’est pas possible de supprimer un critère de sélection. L’instruction :
entraîne la suppression du filtre sur l’indicateur de suppression logique et sur le tenant.
Nouveauté apportée par les filtres de requête nommés
Cette nouveauté permet de définir les filtres de requête de la manière suivante :
modelBuilder.Entity<Project>()
.HasQueryFilter("SoftDelete", p => !p.IsDeleted)
.HasQueryFilter("Tenant", p => p.TenantId == _tenantId)
Les filtres sont désormais nommés. Ainsi, si nous avons besoin d’obtenir tous les projets d’un tenant, nous pouvons écrire la requête LINQ suivante :
La requête SQL envoyée au serveur est la suivante :