Accéder à la valeur d’une expression membre

Si j’ai un produit

var p = new Product { Price = 30 }; 

et j’ai la requête linq suivante.

 var q = repo.Products().Where(x=>x.Price == p.Price).ToList() 

Dans un fournisseur IQueryable, je récupère un MemberExpression pour le p.Price qui contient une expression constante, mais je n’arrive pas à récupérer la valeur “30”.

Mise à jour J’ai essayé ceci mais cela ne semble pas fonctionner.

 var memberExpression = (MemberExpression)GetRootConstantExpression(m); var fi = (PropertyInfo)memberExpression.Member; var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null); 

À votre santé.

Vous pouvez comstackr et invoquer une expression lambda dont le corps est l’access au membre:

 private object GetValue(MemberExpression member) { var objectMember = Expression.Convert(member, typeof(object)); var getterLambda = Expression.Lambda>(objectMember); var getter = getterLambda.Comstack(); return getter(); } 

L’évaluation locale est une technique courante lors de l’parsing des arbres d’expression. LINQ to SQL fait exactement cela dans de nombreux endroits.

  MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right; Expression.Lambda(right).Comstack().DynamicInvoke(); 

L’expression constante va pointer vers une classe de capture générée par le compilateur. Je n’ai pas inclus les points de décision, etc., mais voici comment en obtenir 30:

 var p = new Product { Price = 30 }; Expression> predicate = x => x.Price == p.Price; BinaryExpression eq = (BinaryExpression)predicate.Body; MemberExpression productToPrice = (MemberExpression)eq.Right; MemberExpression captureToProduct = (MemberExpression)productToPrice.Expression; ConstantExpression captureConst = (ConstantExpression)captureToProduct.Expression; object product = ((FieldInfo)captureToProduct.Member).GetValue(captureConst.Value); object price = ((PropertyInfo)productToPrice.Member).GetValue(product, null); 

price est maintenant 30 . Notez que je suppose que Price est une propriété, mais en réalité, vous écrirez une méthode GetValue qui gère les propriétés / champs.

q est de type List . La liste n’a pas de propriété Price – uniquement les produits individuels.

Le premier ou le dernier produit aura un prix.

 q.First().Price q.Last().Price 

Si vous savez qu’il n’y en a qu’un dans la collection, vous pouvez également l’aplatir avec Single

 q.Single().Price 

Pouvez-vous utiliser les éléments suivants:

 var price = p.Price; var q = repo.Products().Where(x=>x.Price == price).ToList() 

En utilisant Expression.Lambda(myParameterlessExpression).Comstack().Invoke() présente plusieurs inconvénients:

  • .Comstack() est lent . Cela peut prendre plusieurs millisecondes, même pour les petits fragments d’expression. L’ Invoke est super rapide par la suite, mais ne prend que quelques nanosecondes pour les expressions arithmétiques simples ou les access membres.
  • .Comstack() va générer (émettre) du code MSIL. Cela peut sembler parfait (et explique l’excellente vitesse d’exécution) mais le problème est le suivant: ce code prend de la mémoire, ce qui ne peut être libéré avant la fin de l’application , même lorsque le GC a collecté la référence du délégué!

On peut soit éviter Comstack() pour éviter ces problèmes, soit mettre en cache les delegates compilés pour les réutiliser. Cette petite bibliothèque offre à la fois une interprétation des Expressions et une compilation en cache , où toutes les constantes et fermetures de l’expression sont automatiquement remplacées par des parameters supplémentaires, qui sont ensuite réinsérés dans une fermeture, qui est renvoyée à l’utilisateur. Les deux processus sont bien testés, utilisés en production, les deux ont leurs avantages et leurs inconvénients les uns contre les autres mais sont bien plus rapides que 100 fois plus rapides que Comstack() et évitent la fuite de mémoire!

Et qu’est-ce que vous essayez d’accomplir exactement?

Parce que pour accéder à la valeur de Price , vous devez faire quelque chose comme:

 var valueOfPrice = q[0].Price; 

Si vous aviez un cours:

 public class Item { public int Id { get; set; } } 

et une instance de l’object:

 var myItem = new Item { Id = 7 }; 

Vous pouvez obtenir la valeur de Id en utilisant une expression à l’aide du code suivant:

 Expression> exp = x => x.Id; var me = exp.Body as MemberExpression; var propInfo = me.Member as PropertyInfo; var value = propInfo.GetValue(myItem, null); 

la valeur contiendra “7”