2010-08-17 8 views
10

J'ai une relation de parent-enfant. Dans un dépôt, je fais ceci:EF prend pour toujours générer cette requête

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 de ids est énorme. Cela génère une instruction SQL simple qui possède une énorme liste d'identifiants "in (1,2,3 ...)", mais cela ne prend pas beaucoup de temps pour s'exécuter tout seul. EF, cependant, prend environ une minute complète juste pour générer la déclaration. Je l'ai prouvé en fixant un point d'arrêt et d'appeler:

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

Cela prend tout le temps. Est-ce que le problème est dans ma dernière déclaration linq? Je ne connais pas d'autre moyen de faire l'équivalent de Child.ChildId dans (ids). Et même si ma déclaration de linq est mauvaise, comment dans le monde cela devrait-il prendre tant de temps?

+0

Publiez votre schéma et le sql généré, vous pouvez obtenir plus d'aide. –

+0

Le schéma ne semble pas avoir d'importance, pas plus que la relation parent/enfant. Même problème en sélectionnant simplement à partir d'une seule table/entité avec une grande liste. – dudeNumber4

+0

Pour toute autre personne rencontrant le problème; de tout ce que je peux dire, il n'y a pas de solution avec EF4. Vous devez recourir à des procédures stockées. Plus d'informations: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/d629c798-db45-4a04-9813-a3b565d87c83 – dudeNumber4

Répondre

2

Réécrivez votre requête dans la syntaxe Lambda et cela réduira le temps de 3 secondes (ou du moins 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))); 
+0

'Select (s => s)' ne fait rien. Finis après le Où. – StriplingWarrior

+0

Droit ... Merci! –

+0

Cela n'a fait aucune différence dans mon cas. – dudeNumber4

4

Malheureusement, les requêtes de construction dans LINQ to Entities est un coup assez lourd, mais je l'ai trouvé enregistre habituellement le temps en raison de la capacité de construire des requêtes de leurs pièces constitutives avant en train de frapper la base de données.

Il est probable que la façon dont ils implémentent la méthode Contient utilise un algorithme qui suppose que Contient est généralement utilisé pour un ensemble de données relativement petit. Selon mes tests, le temps qu'il faut par ID dans la liste commence à monter en flèche à environ 8000.

Ainsi, il peut être utile de diviser votre requête en plusieurs parties. Regroupez-les en groupes de 1000 ou moins et concaténez un groupe d'expressions Where.

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 d'utilisation ne correspond tout simplement pas à la "boîte" du comportement Entity Framework attendu. Si votre liste d'identifiants pouvait commencer comme une requête provenant du même contexte d'entité, Entity Framework aurait fonctionné correctement.

+0

Refusée, mais n'a pas marqué comme réponse. L'utilisation de procs stockés est la réponse, mais je ne peux pas utiliser la solution de requête cassée. Je vais juste utiliser la solution EF rompue ... – dudeNumber4

+0

Je vous encourage à commencer une prime ou à marquer une réponse. Même si vous écrivez votre propre réponse basée sur proc-stock à votre question, la FAQ indique clairement que c'est acceptable.Bien sûr, je tiens également à souligner que non seulement j'ai répondu à la question posée, mais j'ai aussi fourni la solution que vous avez l'intention d'utiliser. – StriplingWarrior

+0

C'est probablement la meilleure réponse que vous obtiendrez. À court de quelqu'un qui sort avec un correctif pour le cadre, c'est la réponse. –

Questions connexes