2012-03-03 9 views
5

Hace poco me topé con el problema para crear dinámicamente expresiones de Linq durante el tiempo de ejecución. La mayoría de los ejemplos que encontré tratan de la tarea bastante simple de simplemente comparar una propiedad de una entidad de base de datos dada con un solo parámetro. De este modo:Creando una expresión de Linq que contenga dinámicamente una subconsulta

Session.Query.Where(m => m.Name.Contains("test")) 

que también podría lograrse con un enfoque mucho más genérico como esto:

var item = Expression.Parameter(typeof (MyClass), "item"); 
var property = Expression.Property(item, "Name"); 
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
var searchExpression = Expression.Constant(searchString, typeof(string)); 
var containsMethodExpression = Expression.Call(property, containsMethod, searchExpression); 
var lambda = Expression.Lambda<Func<MyClass, bool>>(containsMethodExpression, item); 
query = query.Where(lambda);  

Sin embargo, a veces la tarea es algo más complejo y uno quiere alcanzar algo como lo siguiente:

Session.Query.Where(m => m.SpecialProperty.Any(f => f.Name.Contains("test"))); 

Donde "SpecialProperty" es del tipo Lista <> y la propiedad "Nombre" es del tipo cadena.

¿Es posible construir dinámicamente una expresión Linq así y cómo se puede lograr esto? ¿Hay algún problema de rendimiento con respecto a este enfoque?

Respuesta

6

Encontré una solución que funciona en mi caso de uso particular y hace exactamente lo que estaba buscando.

/* 
building expression tree 
example: Session.Query.Where(m => m.SpecialProperty.Any(f => f.Name.Contains("test"))) 
*/ 

var innerItem = Expression.Parameter(typeof(MyInnerClass), "f"); 
var innerProperty = Expression.Property(innerItem, "Name"); 
var innerMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
var innerSearchExpression = Expression.Constant(searchString, typeof(string)); 
var innerMethodExpression = Expression.Call(innerProperty, innerMethod, new[] { innerSearchExpression }); 
var innerLambda = Expression.Lambda<Func<MyInnerClass, bool>>(innerMethodExpression, innerItem); 

var outerItem = Expression.Parameter(typeof(MyOuterClass), "m"); 
var outerProperty = Expression.Property(outerItem, info.Name); 
/* calling a method extension defined in Enumerable */ 
var outerMethodExpression = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(MyInnerClass) }, outerProperty, innerLambda); 
var outerLambda = Expression.Lambda<Func<MyOuterClass, bool>>(outerMethodExpression, outerItem); 
query = query.Where(outerLambda); 

Este enfoque bastante poco elegante se necesita en lugar de la más elegante sola línea LINQ expresión para permitir la parametrización de tipos y nombres de método. Sin embargo, por supuesto, no me importarían otras sugerencias e ideas sobre posibles sanciones de rendimiento.

Es muy probable que este fragmento de código también pueda ayudar a resolver How to produce a Subquery using non-generic Lambda.

Cuestiones relacionadas