调用。包含表达式<func <t,string >>

时间:2018-08-29 18:01:31

标签: c# entity-framework linq functional-programming

我想实现以下方法

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

因此,如果我想(例如)找到名字包含“ Mark”的人或父亲名字包含“ Mark”的人,我可以执行以下操作:

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

我找到了一些使我 close 与我想要的here相近的地方,但我希望能够使用Func<T,string>来指定要过滤的字段而不只是字段名称。 (编辑:我希望能够指定将要检查的-values,而不是字段名称

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 :(得分:3)

一些通用操作确实有助于解决大多数与表达有关的问题。在这种情况下,有两个真正对您有帮助的地方:能够将两个表达式组合在一起,并计算Ors 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);
    }
}

将“或”或“ 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);
}