Как динамически создать предикат Expression<Func <MyClass, bool >> из Expression<Func <MyClass, string >>?
Я пытаюсь добавить, где предикаты, и моя цель состоит в том, чтобы создать то же выражение, что и:
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"))
Однако это неверно, так как параметр для Name и 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 ответа
Трудно смешать сгенерированные компилятором деревья выражений и созданные вручную, именно из-за такого рода вещей - извлечь 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
а не string
,
Я попробовал, и, кажется, сработало, когда я позвонил lambda.Compile
и выполнил его на пару образцов Service
объекты...
Вы можете создать дерево выражений для обнуляемых типов, предположим, что у вас есть обнуляемое поле BoardId, вы можете динамически создавать дерево выражений, как это
var nameValue="BoardId=111";
вам нужно определить первый тип свойства, независимо от того, является ли он обнуляемым
Ниже приведен код создания динамического выражения для типов NULL и NULL.
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 - Третий шаг - проверить, что значение не обнуляемо, а не строка, а затем создать выражение, используя равное