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é.
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.
MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
Expression.Lambda(right).Compile().DynamicInvoke();
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.
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
Pouvez-vous utiliser les éléments suivants:
var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
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!
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 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"