Как мне динамически создать выражение> Предикат из выражения>? - PullRequest
39 голосов
/ 23 февраля 2011

Я пытаюсь добавить, где предикаты, и моя цель состоит в том, чтобы создать такое же выражение, как:

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

У меня есть следующий код:

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

Это создает следующий вывод:

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

Однако это ошибка, поскольку параметр для Имя и Пространство имен не совпадает.Если я изменю один из селекторов выражений на:

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

Вывод будет:

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

Как создать действительное выражение с использованием sel1 и sel2 ?

ОБНОВЛЕНИЕ (28 фев 2011)

Я решил это, создав выражения выражений: Expression.Invoke, поэтому лямбда-выражения sel1 иsel2 не обязательно должен быть 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);
}

Ответы [ 2 ]

66 голосов
/ 23 февраля 2011

Трудно смешать сгенерированные компилятором деревья выражений и созданные вручную, именно из-за такого рода вещей - извлечь ParameterExpressions довольно сложно.Итак, начнем с нуля:

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

Один важный аспект, который я изменил, - это тип, переданный в Expression.Parameter - он, безусловно, выглядит так, как будто он должен быть Service, а неa string.

Я попробовал, и, кажется, это сработало, когда я позвонил lambda.Compile и выполнил его на паре примеров Service объектов ...

3 голосов
/ 24 мая 2018

Вы можете создать дерево выражений для обнуляемых типов, предположим, что у вас есть обнуляемое поле BoardId, вы можете динамически создавать дерево выражений, как это

var nameValue = "BoardId = 111";

вам необходимо определить первый тип свойства, независимо от того, является ли он пустым или нет

Ниже кода создайте выражение динамического дерева для типов Nullable и Non Nullable

 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- Это решение сначала проверяет значение Nullable и генерирует выражение. Вот как вы можете определить, является ли тип Nullable. Для этого я создал метод расширения

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

2 - второй шаг - проверить тип, если его строка, а затем создать выражение для строки.

3 - Третий шаг - проверить, является ли значение не обнуляемым, не строковым, а затем создать выражение, используя равное

...