Compiler Automatiquement Les Requêtes Linq
Nous avons constaté que la compilation de nos requêtes Linq est beaucoup, beaucoup plus rapide que d'avoir à compiler à chaque fois, donc nous aimerions commencer à utiliser les requêtes compilées. Le problème est que cela rend le code plus difficile à lire, car la syntaxe de la requête est éteint dans un autre fichier, de là où il est utilisé.
Il m'est apparu qu'il pourrait être possible d'écrire une méthode (ou une extension de la méthode) qui utilise la réflexion pour déterminer quelles requêtes sont passés dans et cache les versions compilées automatiquement pour une utilisation dans l'avenir.
var foo = (from f in db.Foo where f.ix == bar select f).Cached();
Cached()
devra refléter la requête de l'objet passé en et de déterminer la /les table(s) sélectionné et les types de paramètres de la requête. De toute évidence, la réflexion est un peu lent, de sorte qu'il pourrait être préférable d'utiliser des noms de l'objet dans le cache (mais que vous souhaitez toujours avoir à utiliser la réflexion la première fois à la compilation de la requête).
var foo = (from f in db.Foo where f.ix == bar select f).Cached("Foo.ix");
Quelqu'un a une expérience avec cette, ou de savoir si c'est encore possible?
Mise à JOUR: Pour ceux qui ne l'ont pas vu, vous pouvez compiler des requêtes LINQ à SQL avec le code suivant:
public static class MyCompiledQueries
{
public static Func<DataContext, int, IQueryable<Foo>> getFoo =
CompiledQuery.Compile(
(DataContext db, int ixFoo) => (from f in db.Foo
where f.ix == ixFoo
select f)
);
}
Ce que j'essaie de faire est d'avoir un cache de ces Func<>
objets que je peux appeler automatiquement après la compilation de la requête la première fois autour.
- C'est une source de confusion question, parce que vous semblez être l'amalgame entre LINQ et LINQ to SQL (qui génère en outre, les compile et les caches des plans d'exécution de derrière les scènes à chaque fois qu'une requête est exécutée). Si vous vous posez à propos de SQL Server est compilé les plans d'exécution, il n'y a aucun moyen (que je suis au courant) pour les compiler et de les garder en cache d'autres que de les exécuter.
- Cela n'a rien à voir avec SQL Server. LINQ to SQL compile les requêtes, ce qui peut prendre un certain temps-à partir de deux LINQ syntaxes (chaînage ou de type SQL) pour SQL chaque fois que ces requêtes sont exécutées. Lire le lien en haut pour en savoir plus.
- Un problème que j'ai trouvé avec l'aide de requêtes compilées avec L2S dans une application web, c'est que pour la compilation vous avez besoin de la passer à l'instance de la DataContext - pour une application web, cela signifie que vous avez besoin d'une DataContext pour l'ensemble du site - qui, en retour, m'a causé quelques grands multithread problèmes lorsque le site a commencé à avoir une grosse charge. Vraiment, je n'aime vraiment pas la façon dont vous avez à passer le datacontext instance lors de la compilation de la requête...
- Vous ne passez pas dans un DataContext lors de la compilation de la requête. Voir ma réponse ci-dessous; vous fait passer dans un délégué qui prend un DataContext (et d'autres paramètres) et retourne une
IQueryable<T>
. - Avez-vous jamais les mettre en œuvre ce chemin? Si oui, comment avez-vous gérer les différents niveaux de DataLoadOptions?
Vous devez vous connecter pour publier un commentaire.
Vous ne pouvez pas avoir des méthodes d'extension appelée sur anonyme lambda expressions, de sorte que vous aurez envie d'utiliser une classe de Cache. Afin de bien mettre en cache une requête, vous aurez également besoin d "ascenseur' tous les paramètres (y compris votre DataContext) dans les paramètres de votre expression lambda. Il en résulte très détaillé de l'utilisation, comme:
Afin de nettoyer tout ça, on peut instancier un QueryCache par le contexte, si nous le rendre non-statique:
Alors nous pouvons écrire un Cache méthode qui va nous permettre d'écrire ce qui suit:
Aucun argument dans votre requête devra également être levé:
Voici la QueryCache mise en œuvre j'ai fait la maquette:
Cela peut être étendu pour prendre en charge plus d'arguments. Le grand bit est qu'en transmettant les valeurs de paramètres dans la mémoire Cache de la méthode elle-même, vous obtenez de typage implicite de l'expression lambda.
EDIT: Notez que vous ne pouvez pas appliquer de nouveaux opérateurs pour les requêtes compilées.. Spécifiquement vous ne pouvez pas faire quelque chose comme ceci:
Donc, si vous envisagez sur la pagination d'une requête, vous devez le faire dans l'opération de la compilation au lieu de le faire plus tard. Ceci est nécessaire non seulement pour éviter une exception, mais également en conformité avec le point de l'ensemble de Skip/Prendre (pour éviter de se retrouver toutes les lignes de la base de données). Ce modèle devrait fonctionner:
Une autre approche de la pagination serait de revenir un
Func
:Ce modèle est utilisé comme:
QueryCache
un peu comme l'exemple de code que j'ai ci-dessus, et lakey
local var est définie à une interprétation littérale de la requête LINQ avec pas de SQL, comme mon commentaire ci-dessus. Définissez un point d'arrêt sur lalock
lignes dans QueryCache et voir ce que vous obtenez.Puisque personne n'est d'essayer, je vais donner un coup de feu. Peut-être qu'on peut à la fois faire ce travail en quelque sorte. Voici ma tentative à cet égard.
Je le configurer à l'aide d'un dictionnaire, je ne suis pas aussi à l'aide de DataContext bien que c'est trivial, je crois.
maintenant, cela me permet de faire ce
impatient de discussion sur ce sujet, pour développer cette idée.
Expression
sur unIEnumerable
il n'y a pas de différence entre l'expression.Compiler() et à laFunc
code IL qui auraient été engendrés si vous n'étiez pas en train de demander unExpression
. En fait, exp.Compiler() sera probablement plus lent car vous êtes absent beaucoup d'optimisations du compilateur.var results = list.Cache("To", x => x.Where(y => y.Contains("To")))
sera plus lent que de simplement appelervar results = list.Where(y => y.Contains("To"))
.Pour l'avenir de la postérité : .NET Framework 4.5 va le faire par défaut (d'après une diapositive dans une présentation, je viens de regarder).