Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
En règle générale, Entity Framework Core tente d’évaluer une requête sur le serveur autant que possible. EF Core convertit des parties de la requête en paramètres, qu’elle peut évaluer côté client. Le reste de la requête (ainsi que les paramètres générés) est donné au fournisseur de base de données pour déterminer la requête de base de données équivalente à évaluer sur le serveur. EF Core prend en charge l’évaluation partielle du client dans la projection de niveau supérieur (essentiellement, le dernier appel à Select()). Si la projection de niveau supérieur dans la requête ne peut pas être traduite sur le serveur, EF Core récupère toutes les données requises du serveur et évalue les parties restantes de la requête sur le client. Si EF Core détecte une expression, à tout endroit autre que la projection de niveau supérieur, qui ne peut pas être traduite sur le serveur, elle lève une exception d’exécution. Découvrez comment les requêtes fonctionnent pour comprendre comment EF Core détermine ce qui ne peut pas être traduit sur le serveur.
Remarque
Avant la version 3.0, Entity Framework Core a pris en charge l’évaluation du client n’importe où dans la requête. Pour plus d’informations, consultez la section versions précédentes.
Conseil / Astuce
Vous pouvez afficher l’exemple de cet article sur GitHub.
Évaluation du client dans la projection de niveau supérieur
Dans l’exemple suivant, une méthode d’assistance est utilisée pour normaliser les URL pour les blogs, qui sont retournées à partir d’une base de données SQL Server. Étant donné que le fournisseur SQL Server n’a aucun aperçu de la façon dont cette méthode est implémentée, il n’est pas possible de le traduire en SQL. Tous les autres aspects de la requête sont évalués dans la base de données, mais le passage du résultat retourné par URL à travers cette méthode est effectué sur le client.
var blogs = await context.Blogs
.OrderByDescending(blog => blog.Rating)
.Select(
blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
.ToListAsync();
public static string StandardizeUrl(string url)
{
url = url.ToLower();
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", url);
}
return url;
}
Évaluation du client non prise en charge
Bien que l’évaluation du client soit utile, cela peut entraîner des performances médiocres parfois. Considérez la requête suivante, dans laquelle la méthode auxiliaire est désormais utilisée dans un filtre WHERE. Étant donné que le filtre ne peut pas être appliqué dans la base de données, toutes les données doivent être extraites en mémoire pour appliquer le filtre sur le client. En fonction du filtre et de la quantité de données sur le serveur, l’évaluation du client peut entraîner des performances médiocres. Entity Framework Core bloque donc l’évaluation du client et lève une exception d’exécution.
var blogs = await context.Blogs
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Évaluation explicite du client
Vous devrez peut-être forcer explicitement l’évaluation du client dans certains cas comme suit :
- La quantité de données est faible pour que l'évaluation sur le client n'entraîne pas une lourde pénalité de performance.
- L’opérateur LINQ utilisé n’a aucune traduction côté serveur.
Dans ce cas, vous pouvez opter explicitement pour l’évaluation du client en appelant des méthodes telles que AsEnumerable ou ToList (AsAsyncEnumerable ou ToListAsync pour async). En utilisant AsEnumerable, vous diffuseriez les résultats en continu, mais en utilisant ToList, cela entraînerait une mise en mémoire tampon en créant une liste, ce qui prend également de la mémoire supplémentaire. Bien que si vous énumérez plusieurs fois, le stockage des résultats dans une liste permet d’en savoir plus, car il n’existe qu’une seule requête dans la base de données. Selon l’utilisation particulière, vous devez évaluer la méthode qui est plus utile pour le cas.
var blogs = context.Blogs
.AsAsyncEnumerable()
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Conseil / Astuce
Si vous utilisez AsAsyncEnumerable et souhaitez composer la requête plus loin côté client, vous pouvez utiliser la bibliothèque System.Interactive.Async qui définit des opérateurs pour les énumérables asynchrones. Pour plus d’informations, consultez les opérateurs LINQ côté client.
Fuite de mémoire potentielle dans l’évaluation du client
Étant donné que la traduction et la compilation des requêtes sont coûteuses, EF Core met en cache le plan de requête compilé. Le délégué mis en cache peut utiliser du code client lors de l’évaluation, par le client, de la projection de niveau supérieur. EF Core génère des paramètres pour les parties évaluées par le client de l’arborescence et réutilise le plan de requête en remplaçant les valeurs des paramètres. Toutefois, certaines constantes de l’arborescence d’expressions ne peuvent pas être converties en paramètres. Si le délégué mis en cache contient de telles constantes, ces objets ne peuvent pas être ramassés par le ramasse-miettes, car ils sont toujours référencés. Si un tel objet contient un DbContext ou d’autres services, il peut entraîner l’augmentation de l’utilisation de la mémoire de l’application au fil du temps. Ce comportement est généralement un signe d’une fuite de mémoire. EF Core lève une exception chaque fois qu'il rencontre des constantes d'un type qui ne peut pas être mappé à l'aide du fournisseur de base de données actuel. Les causes courantes et leurs solutions sont les suivantes :
- Utilisation d’une méthode d’instance : lorsque vous utilisez des méthodes d’instance dans une projection cliente, l’arborescence d’expressions contient une constante de l’instance. Si votre méthode n’utilise aucune donnée de l’instance, envisagez de rendre la méthode statique. Si vous avez besoin de données d’instance dans le corps de la méthode, transmettez les données spécifiques en tant qu’argument à la méthode.
-
Passage d’arguments constants à la méthode : ce cas se produit généralement à l’aide
thisd’un argument à la méthode cliente. Envisagez de fractionner l’argument en plusieurs arguments scalaires, qui peuvent être mappés par le fournisseur de base de données. - Autres constantes : si une constante est rencontrée dans un autre cas, vous pouvez évaluer si la constante est nécessaire dans le traitement. S’il est nécessaire d’avoir la constante, ou si vous ne pouvez pas utiliser une solution à partir des cas ci-dessus, créez une variable locale pour stocker la valeur et utiliser la variable locale dans la requête. EF Core convertit la variable locale en paramètre.
Versions précédentes
La section suivante s’applique aux versions EF Core antérieures à la version 3.0.
Les versions antérieures d'EF Core ont pris en charge l'évaluation côté client dans n'importe quelle partie de la requête, et pas seulement dans la projection de niveau supérieur. C’est pourquoi les requêtes similaires à celles publiées dans la section d’évaluation du client non pris en charge ont fonctionné correctement. Étant donné que ce comportement peut entraîner des problèmes de performances inaperçus, EF Core a enregistré un avertissement d’évaluation du client. Pour plus d’informations sur l’affichage de la sortie de journalisation, consultez Journalisation.
Si vous le souhaitez, EF Core vous a permis de modifier le comportement par défaut pour lever une exception ou ne rien faire lors de l’évaluation du client (sauf dans la projection). Le comportement de levée d’exception le rend similaire au comportement dans la version 3.0. Pour modifier le comportement, vous devez configurer des avertissements lors de la configuration des options de votre contexte , généralement dans DbContext.OnConfiguringou si Startup.cs vous utilisez ASP.NET Core.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}