expressions create c# linq expression-trees linq-expressions

c# - create - ¿Cómo creo dinámicamente un predicado Expression<Func<MyClass, bool>> de Expression<Func<MyClass, string>>?



lambda expressions c# where (2)

Es difícil mezclar árboles de expresión generados por el compilador y hechos a mano, precisamente por este tipo de cosas: extraer los ParameterExpressions es complicado. Comencemos desde cero:

ParameterExpression argParam = Expression.Parameter(typeof(Service), "s"); Expression nameProperty = Expression.Property(argParam, "Name"); Expression namespaceProperty = Expression.Property(argParam, "Namespace"); var val1 = Expression.Constant("Modules"); var val2 = Expression.Constant("Namespace"); Expression e1 = Expression.Equal(nameProperty, val1); Expression e2 = Expression.Equal(namespaceProperty, val2); var andExp = Expression.AndAlso(e1, e2); var lambda = Expression.Lambda<Func<Service, bool>>(andExp, argParam);

Un aspecto importante que he cambiado es el tipo pasado a Expression.Parameter - ciertamente parece que debería ser un Service lugar de una string .

Lo probé y pareció funcionar cuando llamé a lambda.Compile y lo lambda.Compile en un par de objetos de Service de muestra ...

Intento agregar los predicados y mi objetivo es crear la misma expresión que:

Services.Where(s => s.Name == "Modules" && s.Namespace == "Namespace");

Tengo el siguiente código:

Expression<Func<Service,string>> sel1 = s => s.Name; Expression<Func<Service,string>> sel2 = s => s.Namespace; var val1 = Expression.Constant("Modules"); var val2 = Expression.Constant("Namespace"); Expression e1 = Expression.Equal(sel1.Body, val1); Expression e2 = Expression.Equal(sel2.Body, val2); var andExp = Expression.AndAlso(e1, e2); ParameterExpression argParam = Expression.Parameter(typeof(string), "s"); var lambda = Expression.Lambda<Func<string, bool>>(andExp, argParam);

Esto crea el siguiente resultado:

s => ((s.Name == "Modules") AndAlso (s.Namespace == "Namespace"))

Sin embargo, esto es incorrecto ya que el parámetro para Nombre y Espacio de nombre no es el mismo. Si cambio uno de los selectores de expresiones a:

Expression<Func<Service,string>> sel2 = srv => srv.Namespace;

El resultado será:

s => ((s.Name == "Modules") AndAlso (srv.Namespace == "Namespace"))

¿Cómo puedo crear una expresión válida con el uso de sel1 y sel2 ?

ACTUALIZACIÓN (28 de febrero de 2011)

Lo resolví creando expresiones de invocación: Expression.Invoke para que las expresiones lambda sel1 y sel2 no sean necesarias deben ser MemberExpression:

Expression<Func<Service,string>> sel1 = s => s.Name; Expression<Func<Service,string>> sel2 = srv => srv.Namespace; var val1 = Expression.Constant("Modules"); var val2 = Expression.Constant("Namespace"); Expression<Func<Service, bool>> lambda = m => true; var modelParameter = lambda.Parameters.First(); // sel1 predicate { var invokedExpr = Expression.Invoke(sel1, modelParameter); var binaryExpression = Expression.Equal(invokedExpr, val1); lambda = Expression.Lambda<Func<Service, bool>>(Expression.AndAlso(binaryExpression, lambda.Body), lambda.Parameters); } // sel2 predicate { var invokedExpr = Expression.Invoke(sel2, modelParameter); var binaryExpression = Expression.Equal(invokedExpr, val2); lambda = Expression.Lambda<Func<Service, bool>>(Expression.AndAlso(binaryExpression, lambda.Body), lambda.Parameters); }


Puede crear un árbol de Expresión para tipos anulables, suponga que tiene un campo nulo, BoardId, puede crear un árbol de expresiones dinámicamente como este

var nameValue = "BoardId = 111";

necesita determinar el primer tipo de propiedad, ya sea que sea Nullable o no

A continuación, el código crea una expresión de árbol dinámico para tipos anulables y no vinculables.

public static Expression<Func<T, bool>> BuildWhereExpression<T>(string nameValueQuery ) where T : class { Expression<Func<T, bool>> predicate = null; PropertyInfo prop = null; var fieldName = nameValueQuery.Split("=")[0]; var fieldValue = nameValueQuery.Split("=")[1]; var properties = typeof(T).GetProperties(); foreach (var property in properties) { if (property.Name.ToLower() == fieldName.ToLower()) { prop = property; } } if (prop != null) { var isNullable = prop.PropertyType.IsNullableType(); var parameter = Expression.Parameter(typeof(T), "x"); var member = Expression.Property(parameter, fieldName); if (isNullable) { var filter1 = Expression.Constant( Convert.ChangeType(fieldValue, member.Type.GetGenericArguments()[0])); Expression typeFilter = Expression.Convert(filter1, member.Type); var body = Expression.Equal(member, typeFilter); predicate = Expression.Lambda<Func<T, bool>>(body, parameter); } else { if (prop.PropertyType == typeof(string) && likeOerator.ToLower() == "like") { var parameterExp = Expression.Parameter(typeof(T), "type"); var propertyExp = Expression.Property(parameterExp, prop); MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var someValue = Expression.Constant(fieldValue, typeof(string)); var containsMethodExp = Expression.Call(propertyExp, method, someValue); predicate = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp); } else { var constant = Expression.Constant(Convert.ChangeType(fieldValue, prop.PropertyType)); var body = Expression.Equal(member, constant); predicate = Expression.Lambda<Func<T, bool>>(body, parameter); `enter code here` } } } return predicate; }

1- Esta Solución primero verifica el valor Nullable y genera la expresión. Esta es la forma en que puede determinar si el tipo es Nullable. Creé un método de extensión para ese propósito

public static bool IsNullableType(this Type type) { return type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(Nullable<>))); }

2- El segundo paso es verificar el tipo si su cadena luego crea una expresión para una cadena.

3- El tercer paso es verificar que value no es nulo, no string, luego crear una expresión usando equal