2016-06-01 4 views
0

Je veux exécuter la méthode linq sur iqueryable avec un arbre d'expression à partir de la fonction où je passe le nom de la méthode linq et le nom de la propriété. Mais ma méthode exemple fonctionne uniquement avec les propriétés mappées. Il jette une exception quand j'essaie par exemple de trouver max de propriété calculée.Entity Framework exécute une requête avec des propriétés non mappées. Arbre d'expression

Mes classes:

public partial class Something 
    { 
     public int a { get; set; } 
     public int b { get; set; } 
    } 

    public partial class Something 
    { 
     public int calculated { get { return a * b; } } 
    } 

Méthode d'échantillonnage:

public static object ExecuteLinqMethod(IQueryable<T> q, string Field, string Method) 
    { 
     var param = Expression.Parameter(typeof(T), "p"); 

     Expression prop = Expression.Property(param, Field); 

     var exp = Expression.Lambda(prop, param); 

      Type[] types = new Type[] { q.ElementType, exp.Body.Type }; 
      var mce = Expression.Call(typeof(Queryable),Method,types,q.Expression,exp); 

      return q.Provider.Execute(mce); 
    } 
+1

Pourquoi pensez-vous que l'arbre d'expression peut vous permettre de faire quelque chose qui n'est pas supporté par le fournisseur de requêtes EF? –

+0

J'ai besoin de la méthode universelle pour trouver max, min, sum etc. des propriétés mappées et non mappées. Avec le nom donné de la fonction et le nom de la propriété en tant que paramètres de cette méthode. Cela peut être fait par l'arbre d'expression ou d'une autre manière. –

+0

Alors pourquoi ne trouvez-vous pas "l'autre chemin" d'abord, puis demandez l'aide de l'arbre d'expression si nécessaire. Je suppose que le principal problème est de savoir comment déterminer si le champ est mappé ou non. Et même si vous faites cela, puisqu'il n'y a aucun moyen de savoir ce que la propriété calculée utilise, la seule façon de l'exécuter sera de l'exécuter par rapport à 'IEnumerable '. –

Répondre

0

Pour pouvoir interroger sur les propriétés calculées, vous disposez d'au moins 2 options:

1) vous amassez les valeurs calculées valeurs dans la base de données avec les lignes (ou dans une table différente), et les utiliser dans vos requêtes bien sûr cela nécessite le changement de modèle de données, et la redondance dans les données, mais est le moyen le plus performant. 2) vous devez être capable d'exprimer la façon dont vous "calculez" les propriétés d'une manière que sql comprendra, ce qui signifie que la propriété doit être remplacée par un linq. expression dans la requête finale. J'ai trouvé en 2009 un article incroyable d'Eric Lippert sur l'enregistrement de ces propriétés, mais je ne le trouve plus. En tant que tel ici est un link à un autre, qui a la même idée. Fondamentalement, vous définissez votre calcul comme un arbre d'expression, et utilisez la version compilée dans votre code.

Pour le rendre plus pratique, vous attribuer votre propriété avec un

[AttributeUsage(AttributeTargets.Property)] 
class CalculatedByAttribute: Attribute 
{ 
    public string StaticMethodName {get; private set;} 
    public CalculatedByAttribute(string staticMethodName) 
    { 
     StaticMethodName = staticMethodName; 
    } 
} 

Comme:

public partial class Something 
{ 
    [CalculatedBy("calculatedExpression")] 
    public int calculated { get { return calculatedExpression.Compile()(this); } } 
    public static Expression<Func<Something, int>> calculatedExpression = s => s.a * s.b; 
} 

(bien sûr vous pouvez mettre en cache la compilation) :)

Puis, en votre méthode, si la propriété a votre attribut, vous obtenez la valeur de la propriété statique et l'utilisez dans vos requêtes. Quelque chose le long:

public static object ExecuteLinqMethod<T>(IQueryable<T> q, string Field, string Method) 
{ 
    var propInfo = typeof(T).GetProperty(Field); 
    LambdaExpression exp; 
    var myAttr = propInfo.GetCustomAttributes(typeof(CalculatedByAttribute), true).OfType<CalculatedByAttribute>().FirstOrDefault(); 
    if (myAttr != null) 
     exp = (LambdaExpression)typeof(T).GetField(myAttr.StaticMethodName, BindingFlags.Static | BindingFlags.Public).GetValue(null); 
    else 
    { 
     var param = Expression.Parameter(typeof(T), "p"); 
     Expression prop = Expression.Property(param, Field); 
     exp = Expression.Lambda(prop, param); 
    } 

    Type[] types = new Type[] { q.ElementType, exp.Body.Type }; 
    var mce = Expression.Call(typeof(Queryable),Method,types,q.Expression,exp); 

    return q.Provider.Execute(mce); 
} 
+0

Merci! Cela fonctionne pour le calcul des propriétés mappées, mais je ne sais pas quoi faire avec le calcul basé sur des champs mappés et calculés. Lorsque j'essaie d'utiliser l'expression imbriquée, j'obtiens une erreur 'Le type de noeud d'expression LINQ' Invoke 'n'est pas supporté dans LINQ to Entities.'.Par exemple, j'ai essayé quelque chose comme ceci: [CalculatedBy ("calculateExpression1")] public int calculate1 {get {return calculateExpression1.Compile() (this); }} public static Expression > calculateExpression1 = s => s.a * calculateExpression.Compile() (s); –

+0

Vous êtes sûr que c'était moi? 2009 était il y a longtemps, mais je ne me souviens pas avoir écrit un tel article. Mon collègue de Microsoft, Matt Warren, a écrit des articles sur ce genre de choses dans la journée; peut-être que vous pensez à l'un d'entre eux? –

+0

@AdamBubula: si vous avez besoin de construire des scénarios plus complexes, consultez cette réponse: http://stackoverflow.com/questions/29448432/pass-expression-parameter-as-argument-to-another-expression/29471092#29471092 – MBoros