web-dev-qa-db-fra.com

Vérifier si la liste contient un élément d'une autre liste dans Entity Framework

J'ai une personne d'entité qui a une liste d'emplacements qui lui sont associés. Je dois interroger la table des personnes et obtenir tous ceux qui ont au moins un emplacement dans une liste d'emplacements (critères). Ce qui suit fonctionne mais est très inefficace:

var searchIds = new List<int>{1,2,3,4,5};
var result = persons.Where(p => p.Locations.Any(l => searchIds.Any(id => l.Id == id)));

Cela fonctionne bien pour les petites listes (disons 5-10 searchIds et une personne avec 5-10 emplacements. Le problème est que certaines personnes peuvent avoir 100 emplacements et une recherche peut également être pour 100 emplacements à la fois. Lorsque j'ai essayé d'exécuter ce qui précède EF a en fait produit une instruction SQL 2000+ et a échoué car il était trop profondément imbriqué. Bien que l'imbrication soit déjà un problème en soi, même si cela fonctionnerait, je ne serais toujours pas très arrivé avec une instruction SQL 2000+.

Remarque: le vrai code comprend également plusieurs niveaux et relations parent-enfant, mais j'ai réussi à le réduire à cette structure assez plate en utilisant uniquement des identifiants, au lieu d'objets complets

Quelle serait la meilleure façon d'y parvenir dans EF?

27
Kenneth

Je proposerai:

var searchIds = new List<int>{1,2,3,4,5};
var result = persons.Where(p => p.Locations.Any(l => searchIds.Contains(l.Id)));

Contains sera traduit en instruction IN.

Gardez à l'esprit que la liste des identifiants va dans l'instruction sql. Si votre liste d'ID est énorme, vous finirez par avoir une énorme requête.

57
Ivo

Essayez de basculer vers des jointures au lieu de procéder à une collecte massive de données:

var searchIds = new List<int>{1,2,3,4,5};
var results = (from p in persons
               join l in Location on p.PersonId equals l.PersonId
               where searchIds.Contains(l.Id)
               select p).Distinct().ToList();

Évidemment, corrigez cette ligne pour qu'elle corresponde à vos classes et/ou rejoignez la propriété.

join l in Location on p.PersonId equals l.PersonId

Je m'attendrais à ce que cela génère un plan d'exécution plus convivial.

6
Timeout