【问题标题】:Calling .Contains on the output of a Expression<Func<T,string>>在 Expression<Func<T,string>> 的输出上调用 .Contains
【发布时间】:2018-08-29 18:01:31
【问题描述】:

我想实现下面的方法

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

我发现了一些让我接近的东西here,但我希望能够通过使用Func&lt;T,string&gt; 而不仅仅是名称来指定我想要过滤的字段领域的。 (编辑:我希望能够指定要检查的 -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);

【问题讨论】:

  • 为什么不直接使用static Expression&lt;Func&lt;T, bool&gt;&gt; GetExpression&lt;T&gt;(Func&lt;T, string&gt; propertyName, string propertyValue)
  • EF 需要能够使用函数返回的表达式来创建 SQL 查询。如果我使用对 c# 函数(而不是表达式)的调用来构建表达式,那么 EF 将不知道如何将查询转换为 SQL。
  • 我知道,但没有什么能阻止您在将Func 的结果传递给Expression.Property 之前获得结果
  • @CamiloTerevinto 与接受属性的字符串名称相比,接受返回属性字符串名称的方法有什么好处?
  • @Servy 好吧,“我希望能够使用 Func 而不仅仅是字段名称来指定我想要过滤的字段。”

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


【解决方案1】:

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

【讨论】:

  • 重载的VisitParameter方法调用base.Visit(node)而不是base.VisitParameter(node)有什么原因吗?
猜你喜欢
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多