web-dev-qa-db-fra.com

Accéder à la valeur d'une expression de 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 une expression MemberExpression pour le p.Price qui contient une expression constante, mais il me semble impossible d'obtenir la valeur "30".

Mise à jour J'ai essayé cela, 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é.

59
Schotime

Vous pouvez compiler et appeler une expression lambda dont le corps est l'accès membre:

private object GetValue(MemberExpression member)
{
    var objectMember = Expression.Convert(member, typeof(object));

    var getterLambda = Expression.Lambda<Func<object>>(objectMember);

    var getter = getterLambda.Compile();

    return getter();
}

L'évaluation locale est une technique courante lors de l'analyse d'arbres d'expression. LINQ to SQL fait exactement cela à plusieurs endroits.

95
Bryan Watts
 MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
 Expression.Lambda(right).Compile().DynamicInvoke();
28
Glennular

L'expression constante va pointer sur 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<Func<Product, bool>> 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 écririez une méthode GetValue qui gère la propriété/le champ.

23
Marc Gravell

q est de type List<Product>. La liste n'a pas de propriété Price, mais 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 aussi l'aplatir avec Single.

q.Single().Price
1
Kirk Broadhurst

Pouvez-vous utiliser les éléments suivants:

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

L'utilisation de Expression.Lambda(myParameterlessExpression).Compile().Invoke() présente plusieurs inconvénients: 

  • .Compile() est lent. Plusieurs millisecondes peuvent être nécessaires, même pour de petits fragments d'expression. L’appel Invoke- est ultrarapide par la suite, cependant, ne prend que quelques nanosecondes pour des expressions arithmétiques simples ou des accès membres.
  • .Compile() 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: le code occupe de la mémoire, qui ne peut pas être libérée avant la fin de l’application, même lorsque le GC a collecté la référence de délégué!

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

1

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;
0
Paulo Santos

Si vous avez eu un cours:

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

et une instance de l'objet:

var myItem = new Item { Id = 7 };

Vous pouvez obtenir la valeur de Id en utilisant une expression en utilisant le code suivant:

Expression<Func<Item, int>> 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"

0
the_doc