2012-09-12 2 views
2

J'ai donc récemment découvert que vous pouvez forcer Entity Framework à ne pas traduire votre projection en SQL en spécifiant un Func<T, TResult> à la méthode d'extension .Select() plutôt qu'une expression. Ceci est utile lorsque vous voulez transformer vos données demandées, mais cette transformation devrait se produire dans votre code plutôt que dans la base de données.Cette méthode d'extension matérialise-t-elle efficacement mon IQueryable?

Par exemple, lorsque vous utilisez un nouveau soutien Enum de EF5 et d'essayer de prévoir que d'une propriété de chaîne dans un DTO, alors que ce serait un échec:

results.Select(r => new Dto { Status = r.Status.ToString() }) 

cela fonctionnerait:

results.Select(new Func<Record, Dto>(r => new Dto { Status = r.Status.ToString() })); 

parce que dans le premier cas (expression), EF ne peut pas comprendre comment traduire Status.ToString() en quelque chose que la base de données SQL pourrait exécuter, mais selon this article Les prédicats Func ne sont pas traduits.

Une fois que j'ai eu ce travail, ce ne fut pas beaucoup d'un saut pour créer la méthode d'extension suivante:

public static IQueryable<T> Materialize<T>(this IQueryable<T> q) 
{ 
    return q.Select(new Func<T, T>(t => t)).AsQueryable(); 
} 

Donc, ma question est - ce qu'il ya des pièges que je devrais se méfier lors de l'utilisation de cette ? Y a-t-il un impact sur les performances - soit en injectant cette projection «do-nothing» dans le pipeline de requêtes, soit en empêchant EF d'envoyer la clause .Where() au serveur et d'envoyer ainsi tous les résultats sur le réseau?

L'intention est d'utiliser encore une méthode .Where() pour filtrer les résultats sur le serveur, mais d'utiliser .Materialize() avant .Select() afin que le fournisseur ne cherche pas à traduire la projection vers SQL Server:

return Context.Record 
    .Where(r => // Some filter to limit results) 
    .Materialize() 
    .Select(r => // Some projection to DTO, etc.); 
+0

Ça a l'air bien. 'Where' devrait être envoyé à la base de données. Cependant, pourquoi ne pas simplement utiliser 'ToList()' au lieu de 'Materialize'? –

Répondre

2

simplement à l'aide AsEnumerable devrait faire la même chose:

return Context.Record 
       .Where(r => // Some filter to limit results) 
       .AsEnumerable() // All extension methods now accept Func instead of Expression 
       .Select(r => // Some projection to DTO, etc.); 

Il n'y a aucune raison dans votre méthode Materialise pour revenir à IQueryable parce que je t n'est pas un IQueryable traduit plus à une autre requête. C'est juste IEnumerable.

En termes de performances, vous devriez être OK. Tout ce qui précède la matérialisation est évalué dans la base de données et tout après la matérialisation dans votre code. De plus, dans votre exemple et dans votre exemple, la requête a encore reporté l'exécution - elle n'est pas exécutée tant que quelque chose n'énumère pas la requête.

0

Il y a cependant un problème: le nombre de colonnes récupérées sur le client. Dans le premier cas, ce serait quelque chose comme select Status from Record et dans un autre select Status, field2, field3, field4 from Record

Questions connexes