¿Cómo creo dinámicamente una expresión > predicado de la expresión >?

c# expression-trees linq linq-expressions

Pregunta

Intento agregar donde 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 la siguiente salida:

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

Sin embargo, esto es defectuoso ya que el parámetro para Nombre y Espacio de nombres no es el mismo. Si cambio uno de los selectores de expresión a:

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

La salida 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 necesariamente tengan que ser una Expresión miembro:

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

Respuesta aceptada

Es difícil mezclar los árboles de expresiones generados por compiladores y los hechos a mano, precisamente por este tipo de cosas: extraer las expresiones de parámetros es complicado. Así que vamos a empezar 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 que se pasa a Expression.Parameter : 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 ...


Respuesta popular

Puede crear un árbol de expresiones para tipos que admiten nulos, suponga que tiene un campo de nulo BoardId, puede crear árboles de expresiones dinámicamente de esta manera

var nameValue = "BoardId = 111";

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

Debajo del código, cree una expresión de árbol dinámico para los tipos anulables y no anulables

 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 de Nullable y genera la expresión. Así es como puedes determinar si el tipo es Nullable. He creado 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 de si su cadena y luego crear una expresión para una cadena.

3- El tercer paso es verificar si el valor no es anulable, no cadena, luego crear una expresión usando igual



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow