2012-01-23 16 views
13

Estoy intentando construir una expresión lambda que se combinará con otras en un árbol de expresiones bastante grande para filtrar. Esto funciona bien hasta que necesite filtrar por una propiedad de sub colección.Crear un árbol de expresiones dinámicas para filtrar en una propiedad de la colección

¿Cómo se genera una expresión Lambda que se filtrará utilizando Any() en una propiedad de una colección que es una propiedad del objeto raíz?

Ejemplo:

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

Esto es cómo iba a construir la expresión de forma estática, pero tengo que construirlo de forma dinámica. Perdón por la confusion.

Editar: Aquí hay un fragmento de cómo manejar las expresiones menos complicados:

IQueryable<Office> officeQuery = CurrentDataSource.Offices.AsQueryable<Office>(); 
ParameterExpression pe = Expression.Parameter(typeof(Office), "Office"); 
ParameterExpression tpe = Expression.Parameter(typeof(Trades), "Trades"); 

Expression SimpleWhere = null; 
Expression ComplexWhere = null; 
foreach (ServerSideFilterObject fo in ssfo) 
{ 
    SimpleWhere = null; 
    foreach (String value in fo.FilterValues) 
    { 
     if (!CollectionProperties.Contains(fo.PropertyName)) 
     { 
      //Handle singleton lambda logic here. 
      Expression left = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression right = Expression.Constant(value); 
      if (SimpleWhere == null) 
      { 
       SimpleWhere = Expression.Equal(left, right); 
      } 
      else 
      { 
       Expression e1 = Expression.Equal(left, right); 
       SimpleWhere = Expression.Or(SimpleWhere, e1); 
      } 
     } 
     else 
     { 
      //handle inner Collection lambda logic here. 
      Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
      Expression right = Expression.Constant(value); 
      Expression InnerLambda = Expression.Equal(left, right); 

      //Problem area. 
      Expression OfficeAndProperty = Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)); 
      Expression OuterLambda = Expression.Call(OfficeAndProperty, typeof(Trades).GetMethod("Any", new Type[] { typeof(Expression) }),InnerLambda); 

      if (SimpleWhere == null) 
       SimpleWhere = OuterLambda; 
      else 
       SimpleWhere = Expression.Or(SimpleWhere, OuterLambda); 
     } 
    } 
    if (ComplexWhere == null) 
     ComplexWhere = SimpleWhere; 
    else 
     ComplexWhere = Expression.And(ComplexWhere, SimpleWhere); 
} 
MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { officeQuery.ElementType }, officeQuery.Expression, Expression.Lambda<Func<Office, bool>>(ComplexWhere, new ParameterExpression[] { pe })); 
results = officeQuery.Provider.CreateQuery<Office>(whereCallExpression); 
+0

¿Estás preguntando cómo construir un árbol de expresiones? – SLaks

+0

No estoy seguro de cómo funciona la jerarquía en su ejemplo. ¿Puedes elaborar un poco más sobre eso? ¿Las oficinas son la raíz y luego cada oficina tiene una colección de oficios? ¿Y desea filtrar el nombre del comercio? El filtro es donde estoy un poco perdido. Lo siento. –

+0

No, no estoy seguro de la sintaxis utilizada para construir una expresión con una llamada al método interno y una expresión para un parámetro. En este caso, aparece un error que indica que Any() no se puede encontrar porque mis parámetros no coinciden con la definición. En este caso, no estoy seguro de si es porque estoy sintaxis o si Any() no es compatible con la forma en que lo estoy usando. – George

Respuesta

9

encontrado la solución. No estaba buscando ningún método en el lugar correcto antes.

Expression left = Expression.Property(tpe, typeof(Trades).GetProperty("Name")); 
Expression right = Expression.Constant(value); 
Expression InnerLambda = Expression.Equal(left, right); 
Expression<Func<Trades, bool>> innerFunction = Expression.Lambda<Func<Trades, bool>>(InnerLambda, tpe); 

method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(Trades)); 
OuterLambda = Expression.Call(method, Expression.Property(pe, typeof(Office).GetProperty(fo.PropertyName)),innerFunction); 
0

Lo que indicó como ejemplo trabajará en base a su comentario. He aquí un ejemplo de lo que trabajar con:

Templates.Where(t => t.TemplateFields.Any(f => f.Required == 'Y')) 

Tenemos plantillas que tienen colección específica de campos, y podría exigir esos campos. Así que puedo obtener las plantillas donde cualquier campo es requerido por esa declaración anterior.

Espero que esto ayude ... o al menos confirme lo que estás tratando de hacer. Avíseme si tiene más preguntas sobre esto y lo detallaré.

¡Buena suerte!

+0

Esto es similar a lo que estoy trabajando, pero tengo que construir la expresión lamda de forma dinámica con la reflexión para que pueda garantizar que el filtro incorpora los otros filtros en el conjunto. – George

0

El código proporcionado

CurrentDataSource.Offices.Where(o => o.base_Trades.Any(t => t.Name == "test")) 

debería funcionar, siempre y cuando o.base_Trades implementa IEnumerable<Trade>. Si o.base_Trades solo implementa IEnumerable, necesita usar Cast<Trade>() si puede estar seguro de que todos los elementos en o.base_Trades son de su tipo Trade o OfType<Trade>() si puede haber elementos de otros tipos (incompatibles).

Ese sería el siguiente aspecto:

CurrentDataSource.Offices 
    .Where(o => o.base_Trades.Cast<Trade>.Any(t => t.Name == "test")) 
1

No haga esto, lo que realmente quiere es que use una biblioteca llamada dynamic linq. http://nuget.org/packages/DynamicLINQ

Puede almacenar sus consultas como cadenas y admite consultas muy complejas. Los árboles de expresión son una pesadilla.

+0

que es una biblioteca excelente, la uso también, pero no admite, por ejemplo, clasificar en una propiedad de una colección como 'myIQueryable.OrderBy (x => x.MyCollection.Select (y => y.Myproperty)) ', al menos no puedo hacer que funcione –

Cuestiones relacionadas