expressions create c# linq lambda expression-trees

create - lambda expressions c# where



Acceda al valor de una expresión miembro (8)

Si tengo un producto

var p = new Product { Price = 30 };

y tengo la siguiente consulta linq.

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

En un proveedor de IQueryable, recupero MemberExpression para el precio p que contiene una expresión constante, sin embargo, parece que no puedo recuperar el valor "30".

Actualización He intentado esto, pero parece que no funciona.

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

Aclamaciones.


¿Y qué estás tratando de lograr exactamente?

Porque para acceder al valor de Price , tendrías que hacer algo como:

var valueOfPrice = q[0].Price;


La expresión constante apuntará a una clase de captura generada por el compilador. No he incluido los puntos de decisión, etc., pero he aquí cómo obtener 30 de eso:

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 ahora es 30 . Tenga en cuenta que asumo que Price es una propiedad, pero en realidad escribiría un método GetValue que maneja propiedad / campo.


Puede compilar e invocar una expresión lambda cuyo cuerpo es el acceso de miembro:

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(); }

La evaluación local es una técnica común al analizar árboles de expresión. LINQ to SQL hace esto exactamente en bastantes lugares.


Puedes usar lo siguiente:

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


Si tuvieras una clase:

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

y una instancia del objeto:

var myItem = new Item { Id = 7 };

Puede obtener el valor de Id usando una expresión usando el siguiente código:

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);

el valor contendrá "7"


Usar Expression.Lambda(myParameterlessExpression).Compile().Invoke() tiene varios inconvenientes:

  • .Compile() es lento . Puede llevar varios milisegundos completar incluso fragmentos de expresión pequeños. Sin embargo, la Invoke invocación es súper rápida después, solo toma unos pocos nanosegundos para expresiones aritméticas simples o accesos de miembros.
  • .Compile() generará (emitirá) código MSIL. Eso puede sonar perfecto (y explica la excelente velocidad de ejecución), pero el problema es: ¡ese código ocupa memoria, que no puede liberarse antes de que la aplicación finalice , incluso cuando el GC recolectó la referencia de delegado!

Uno puede evitar Compile() completo para evitar estos problemas o almacenar en caché los delegados compilados para volver a usarlos. This pequeña biblioteca ofrece tanto la interpretación de Expressions como la compilación en caché , donde todas las constantes y cierres de la expresión se reemplazan por parámetros adicionales automáticamente, que luego se vuelven a insertar en un cierre, que se devuelve al usuario. Ambos procesos están bien probados, se usan en producción, ambos tienen sus pros y sus contras, pero son 100 veces más rápidos que Compile() y evitan la pérdida de memoria.


q es del tipo List<Product> . La Lista no tiene una propiedad de Precio, solo los Productos individuales.

El primer o el último Producto tendrá un precio.

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

Si sabes que solo hay uno en la colección, también puedes aplanarlo usando Single

q.Single().Price


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