Générer dynamicment Linq Select

J'ai une database sur laquelle les users peuvent exécuter une variété de calculs. Les calculs se déroulent sur 4 colonnes différentes, chaque calcul n'utilise pas nécessairement toutes les colonnes, ie calcul1 peut se transformer en sql

SELECT SUM(Column1) FROM TABLE WHERE Column1 is not null 

et calcul2 serait

 SELECT SUM(Column2) WHERE Column2 is null 

J'essaie de générer ceci via linq et je peux get datatables correctes en calculant tout à chaque fois comme

 table.Where(x => x.Column1 != null) .Where(x => x.Column2 == null) .GroupBy(x => x.Date) .Select(dateGroup => new { Calculation1 = dateGroup.Sum(x => x.Column1 != null), Calculation2 = dateGroup.Sum(x => x.Column2 == null) } 

Le problème est que mon set de données est très volumineux, et donc je ne veux pas effectuer de calcul à less que l'user ne l'ait demandé. J'ai regardé dynamicment générer des requêtes Linq. Tout ce que j'ai trouvé jusqu'ici est PredicateBuilder et DynamicSQL, qui semblent être seulement utiles pour générer dynamicment le prédicat Where, et coder en dur la requête sql elle-même sous forme de string avec le Sum (Column1) ou Sum (Column2) inséré si nécessaire.

Comment procéder pour append dynamicment les différentes parties de la requête Select dans un type anonyme comme celui-ci? Ou devrais-je envisager une façon entièrement différente de gérer cette

Vous pouvez renvoyer votre requête sans l'exécuter, ce qui vous permettra de choisir dynamicment ce qu'il faut returnner.

Cela dit, vous ne pouvez pas modifier dynamicment un type anonyme lors de l'exécution. Ils sont typés statiquement au moment de la compilation. Cependant, vous pouvez utiliser un object de return différent pour autoriser les propriétés dynamics sans avoir besoin d'une bibliothèque externe.

 var query = table .Where(x => x.Column1 != null) .Where(x => x.Column2 == null) .GroupBy(x => x.Date); 

Vous pouvez ensuite résoudre dynamicment les requêtes avec l'un des éléments suivants:

  1. dynamic

     dynamic returnObject = new ExpandoObject(); if (includeOne) returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.Column1)); if (includeTwo) returnObject.Calculation2 = groupedQuery.Select (q => q.Sum (x => x.Column2)); 
  2. Type de béton

     var returnObject = new StronglyTypedObject(); if (includeOne) returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.BrandId)); 
  3. Dictionary<ssortingng, int>

J'ai résolu cela et je me suis gardé de devoir perdre la security de type avec Dynamic Linq en utilisant une solution de contournement hacky. J'ai un object contenant des booleans qui correspondent aux calculs que je veux faire tels que

 public class CalculationChecks { bool doCalculation1 {get;set;} bool doCalculation2 {get;set;} } 

puis faire une vérification dans mon choix pour savoir si je devrais faire le calcul ou returnner une constante, comme ça

 Select(x => new { Calculation1 = doCalculation1 ? DoCalculation1(x) : 0, Calculation2 = doCalculation2 ? DoCalculation2(x) : 0 } 

Cependant, ceci semble être un cas de bord avec linq à sql ou ef, qui fait que le sql généré continue de faire les calculs spécifiés dans DoCalculation1 () et DoCalculation2 et utilise ensuite une instruction case pour décider si elle va returnner datatables pour moi. Il fonctionne beaucoup plus lentement, 40-60% dans les tests, et le plan d'exécution montre qu'il utilise une requête beaucoup plus inefficace.

La solution à ce problème consistait à utiliser un ExpressionVisitor pour parcourir l'expression et supprimer les calculs si le bool correspondant était false. Le code montrant comment implémenter cet ExpressionVisitor a été fourni par @SsortingplingWarrior sur cette question Avoir l'instruction EF Linq Select Sélectionner une constante ou une fonction

L'utilisation conjointe de ces deux solutions ne crée toujours pas sql qui tourne à 100% de la vitesse de sql simple. Lors des tests, il était inférieur à 10s de sql simple, quelle que soit la taille de l'set de test, et les parties principales du plan d'exécution étaient les mêmes