EF prend toujours pour générer cette requête

voix
10

J'ai une relation de table parent-enfant. Je fais cela dans un dépôt,:

return (from p in _ctx.Parents  
.Include( Children )  
select p).AsQueryable<Parent>();  

Puis, dans un filtre, je veux faire un filtrage du parent par une liste des enfants ids:

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered =
from p in qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) ) select s;  

Ma liste des ids est énorme. Cela génère une instruction SQL simple qui a une liste énorme de ids « dans (1,2,3 ...) », mais il ne prend pas de temps appréciable pour fonctionner par lui-même. EF, cependant, prend environ une minute juste pour générer l'instruction. Je l'ai prouvé en fixant un point d'arrêt et d'appeler:

((ObjectQuery<Parent>)filtered).ToTraceString();

Cela prend tout le temps. Le problème est dans ma dernière déclaration LINQ? Je ne connais pas d'autre façon de faire l'équivalent de Child.ChildId dans (ids). Et même si ma déclaration LINQ est mauvaise, comment dans le monde devrait-il si long?

Créé 17/08/2010 à 16:48
source utilisateur
Dans d'autres langues...                            


2 réponses

voix
4

Malheureusement, la construction de requêtes dans LINQ to Entities est un coup assez lourd, mais je l'ai trouvé économise généralement du temps en raison de la capacité de construire des requêtes de leurs pièces constitutives avant de frapper réellement la base de données.

Il est probable que la façon dont ils mettent en œuvre la méthode Contains utilise un algorithme qui suppose que contient est généralement utilisé pour un ensemble de données relativement faible. D'après mes tests, la quantité de temps nécessaire par ID dans la liste commence à monter en flèche à environ 8000.

Ainsi , il pourrait aider à briser votre requête en morceaux. Les regrouper en groupes de 1000 ou moins, et concaténer un tas d' Whereexpressions.

var idGroups = ids.GroupBy(i => i / 1000);
var q = Parents.Include("Children").AsQueryable();
var newQ = idGroups.Aggregate(q, 
    (s, g) => s.Concat(
                  q.Where(w => w.Children.Any(wi => g.Contains(wi.ChildId)))));

Cela accélère de manière significative, mais il pourrait ne pas être assez significative pour vos besoins, dans ce cas, vous devrez recourir à une procédure stockée. Malheureusement, ce cas particulier de l'utilisation juste ne rentre pas dans « la boîte » du comportement attendu Entity Framework. Si votre liste des ids pourrait commencer une requête à partir du même contexte de l'entité, Entity Framework aurait bien fonctionné.

Créé 24/08/2010 à 01:06
source utilisateur

voix
2

Réécrivez votre requête dans la syntaxe Lambda et il coupera le temps par jusqu'à 3 secondes (ou du moins il l'a fait pour mon projet EF).

return _ctx.Parents.Include( "Children" ).AsQueryable<Parent>();  

et

IQueryable<Parent> qry;  // from above
List<int> ids;  // huge list (8500)
var filtered = qry.Where( p => p.Children.Any(c => ids.Contains(c.ChildId)) );
Créé 24/08/2010 à 00:14
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more