2011-11-08 9 views
0

J'ai commencé à utiliser des requêtes compilées pour augmenter les performances de certaines requêtes LINQ aux entités. Dans un scénario, je n'ai fait que réduire la requête à sa forme la plus élémentaire et l'avoir pré-compilée, puis j'ai ajouté des clauses where basées sur les entrées des utilisateurs.Le chaînage vers une requête compilée perd des avantages en termes de performances

Je semble perdre l'avantage de performance des requêtes compilées dans ce cas particulier. Quelqu'un peut-il expliquer pourquoi?

Voici un exemple de ce que je fais ...

IEnumerable<Task> tasks = compiledQuery.Invoke(context, userId); 

if(status != null) 
{ 
    tasks = tasks.Where(x=x.Status == status); 
} 
if(category != null) 
{ 
    tasks = tasks.Where(x=x.Category == category); 
} 

return tasks; 

Répondre

1

Je pense qu'il est important pour comprendre comment fonctionnent les requêtes compilées dans EF.

Lorsque vous exécutez une requête Entity Framework mappera votre arborescence d'expression à l'aide de votre fichier de mappage (EDMX ou avec le code d'abord vos définitions de modèle) à une requête SQL. Cela peut être une tâche complexe et exigeante. La précompilation stocke les résultats de ces phases de mappage. Ainsi, la prochaine fois que vous cliquerez sur la requête, le SQL sera déjà disponible et il ne lui restera plus qu'à définir les paramètres actuels.

Le problème est qu'une requête précompilée perd son avantage de performance dès que vous modifiez la requête. Disons que vous avez les éléments suivants:

IQueryable query = GetCompiledQuery(); // => db.Tasks.Where(t => t.Id == myId); 
var notModifiedResult = query.ToList(); // Fast 
int ModifiedResult = query.Count(); // Slow 

Avec la première requête, vous aurez tous les avantages de précompilation car EF a le SQL déjà généré pour vous et peut exécuter cette immediat. La deuxième requête perdra la précompilation car elle doit régénérer son SQL.

Si vous souhaitez maintenant exécuter une requête sur notModifiedResult, il s'agira d'une requête Linq To Objects car vous avez déjà exécuté votre SQL dans la base de données et récupéré tous les éléments en mémoire.

Vous pouvez toutefois chaîner des requêtes compilées (c'est-à-dire utiliser une requête compilée dans une autre requête compilée).

Mais votre code nécessiterait une série de requêtes compilées:! - La valeur par défaut - Un état où = null - Une catégorie où = null - Un statut et où les deux catégories = null

1

(Note: Je ne l'ai pas fait un travail EF pour les âges, et il était juste bricolant Ceci est juste une supposition éclairée. ., vraiment)

Cela pourrait être le coupable:

IEnumerable<Task> tasks = compiledQuery.Invoke(context, userId); 

Toute interrogation plus devra être fait dans le processus .NET, pas dans SQL. Tous les résultats possibles devront être récupérés de la base de données et filtrés localement. Essayez ceci:

IQueryable<Task> tasks = compiledQuery.Invoke(context, userId); 

(. En supposant que est valable, bien sûr)

0

La requête compilé ne peut être modifiée, seuls les paramètres peuvent être modifiés. Ce que vous faites ici est en fait l'exécution de la requête, et ensuite le filtrage des résultats.

.Invoke(context, userId); // returns all the results 
.Where(....) // filters on that entire collection 

Vous pouvez voir s'il y a une façon intelligente de retraiter votre requête, de sorte que les paramètres peuvent être inclus dans tous les cas, mais n'a aucun effet. Je n'ai pas travaillé avec des requêtes compilées, désolé à ce sujet, mais cela fonctionne-t-il (en utilisant -1 comme valeur "ignore")?

// bunch of code to define the compiled query part, copied from [msdn][1] 
(ctx, total) => from order in ctx.SalesOrderHeaders 
         where (total == -1 || order.TotalDue >= total) 
         select order); 

Dans SQL, vous faites cela soit en utilisant SQL dynamique, ou ayant une valeur par défaut (ou null) que vous transmettez ce qui indique que le paramètre doit être ignoré

select * from table t 
where 
    (@age = 0  or t.age = @age) and 
    (@weight is null or t.weight = @weight) 
Questions connexes