Вместо того, построение выражения в всем вручную только потому, что один крошечный маленький кусочек является динамическим, вы можете использовать регулярную лямбда, чтобы определить все содержимое, которое на самом деле является статичным, а затем просто заменить маленький бит, который не является.
В частности, общая стратегия, которую вы можете использовать, состоит в том, чтобы иметь лямбда с параметром, представляющим ваш маленький динамический бит, а затем использовать его в регулярной лямбда, а затем заменить все экземпляры этого параметра на динамически построенное выражение:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
string propertyName, string propertyValue)
{
Expression<Func<string, bool>> e = s => s.ToLower().Contains(propertyValue);
var parameter = Expression.Parameter(typeof(T));
var property = Expression.PropertyOrField(parameter, propertyName);
var body = e.Body.Replace(e.Parameters[0], property);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
Это не только упрощает исходный код, но и делает другие изменения в этот статический код, как легко, как редактирование любых обычные старые C# код, не требуя все манипуляции выражения, а также сложность (и потерю статической типизации), который приходит вместе с ним.
Это решение использует следующий метод, чтобы заменить все экземпляры одного выражения с другим:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Другой подход, позволяя вещи, чтобы быть еще более высокий уровень, будет написать Compose
метод, который позволяет вы легко формулируете выражения. Концептуально мы будем иметь две лямбды, и мы хотим, чтобы создать лямбда, который представляет собой вызывающую один и передавая ее результат в другой, а затем возвращает результат:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
Это использует более или менее ту же стратегию, мы использовали выше, но он обобщает его вместо специального корпуса для ваших конкретных выражений.
У нас есть один оставшийся вспомогательный метод, который нужно сделать, прежде чем положить куски вместе; создание метода, который представляет доступ к свойству, как это определено имя строкового свойства:
public static Expression<Func<T, string>> MemberSelector<T>(string propertyName)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(param, propertyName);
return Expression.Lambda<Func<T, string>>(body, param);
}
Использование этих двух вспомогательных методов (которые не зависят от какой-либо конкретной ситуации), теперь мы можем построить лямбда, что мы хочу без каких-либо пользовательских построенного выражения манипуляции:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
string propertyName, string propertyValue)
{
return MemberSelector<T>(propertyName)
.Compose(prop => prop.ToLower().Contains(propertyValue));
}
Добавьте вызов метода к методу 'ToLower', так же, как у вас есть вызов метода для' метода Contains'. Это в основном тот же процесс, просто получите 'MethodInfo' и вызовите' Expression.Call'. – Servy
Кстати, 'p.Name.ToLower(). Содержит (« Пила »)' всегда будет возвращать false. – StriplingWarrior
@StriplingWarrior Ну, если текущая культура не является чем-то довольно незаметным, что на самом деле не делает то, что, по вашему мнению, должно делать (например, учитывая, что «S» - это строчная буква). – Servy