Вызов. Содержит на выходе выражения <Func <T, строка >> - PullRequest
0 голосов
/ 29 августа 2018

Я хочу реализовать следующий метод

public static class Filters
{
   public static Expression<Func<T,bool>> ContainsText<T>(
      string text, params Expression<Func<T,string>>[] fields)
   {
       //..
   }
}

так что, если бы я хотел (например) найти кого-нибудь, чье имя содержит «Марк» или чье имя папа содержит «Марк», я мог бы сделать что-то вроде этого:

var textFilter = Filters.ContainsText<Individual>("Mark", i=>i.FirstName, i=>i.LastName, i=>i.Father.FirstName, i => i.Father.LastName);
var searchResults = _context.Individuals.Where(textFilter).ToList();

Моя конечная цель - создать ContainsTextSpecification для упрощения текстовой фильтрации, которую я могу использовать следующим образом:

var textSpec = new ContainsTextSpecification<Individual>(i=>i.FirstName, i=> i.LastName, i=>i.DepartmentName, i=>i.SSN, i=>i.BadgeNumber);
textSpec.Text = FormValues["filter"];
var results = individuals.Find(textSpec);

Я нашел что-то, что заставляет меня закрыть до того, что я хочу здесь , но я хочу иметь возможность указать поля, которые я хочу отфильтровать, используя Func<T,string> вместо просто название поля. ( edit: я хочу иметь возможность указать проверяемые значения, а не имена полей )

static Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue)
{
    var parameterExp = Expression.Parameter(typeof(T), "type");
    var propertyExp = Expression.Property(parameterExp, propertyName);
    MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    var someValue = Expression.Constant(propertyValue, typeof(string));
    var containsMethodExp = Expression.Call(propertyExp, method, someValue);

    return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
var results = individualRepo.Get(textSpec);

1 Ответ

0 голосов
/ 29 августа 2018

Несколько общих операций действительно помогают решить большинство проблем, связанных с выражениями. В этом случае есть два, которые действительно помогут вам: возможность составить два выражения вместе и вычислить выражение, которое объединяет в себе N предикатов. Если у вас есть эти две операции, ваша реализация становится такой простой, как:

public static Expression<Func<T, bool>> ContainsText<T>(
    string text, params Expression<Func<T, string>>[] fields)
{
    var predicates = fields.Select(projection => projection.Compose(value => value.Contains(text)));            
    return OrAll(predicates);
}

Чтобы составить два выражения вместе, полезно создать метод, который заменяет все экземпляры одного выражения параметра другим:

public static Expression<Func<TSource, TResult>> Compose<TSource, TIntermediate, TResult>(
    this Expression<Func<TSource, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TSource));
    var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param);
    var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue);
    return Expression.Lambda<Func<TSource, TResult>>(body, param);
}
public static Expression ReplaceParameter(this Expression expression,
    ParameterExpression toReplace,
    Expression newExpression)
{
    return new ParameterReplaceVisitor(toReplace, newExpression)
        .Visit(expression);
}
public class ParameterReplaceVisitor : ExpressionVisitor
{
    private ParameterExpression from;
    private Expression to;
    public ParameterReplaceVisitor(ParameterExpression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

To или N предикатов вместе - очень похожий процесс, только для более чем двух значений одновременно:

public static Expression<Func<T, bool>> OrAll<T>(IEnumerable<Expression<Func<T, bool>>> predicates)
{
    var parameter = Expression.Parameter(typeof(T));
    var newBody = predicates.Select(predicate => predicate.Body.ReplaceParameter(predicate.Parameters[0], parameter))
        .DefaultIfEmpty(Expression.Constant(false))
        .Aggregate((a, b) => Expression.OrElse(a, b));
    return Expression.Lambda<Func<T, bool>>(newBody, parameter);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...