c# - linq to entities dynamic where 从 lambdas 构建

标签 c# linq entity-framework lambda expression-trees

我有一组像这样的 lambda

t => t.FirstName
t => t.LastName
t => t.Profession

我想找到一种方法来构建一个表达式,该表达式可用于 Linq to Entities 中的 Where 语句,其中这些 lambda 与使用 string.contains

// a filter is definded by a lambda and the string to compare it with   
var filters = new Dictionary<Expression<Func<Person, string>>, string>();
filters.Add(t => t.FirstName, "Miller");
filters.Add(t => t.Profession, "Engineer");
var filterConstraints = BuildFilterExpression(t => t, filters);
Entities.Persons.Where(filterConstraints).ToList();

public static Expression<Func<TElement, bool>> BuildFilterExpression<TElement>(Dictionary<Expression<Func<TElement, string>>, string> constraints)
{
  List<Expression> expressions = new List<Expression>();

  var stringType = typeof(string);
  var containsMethod = stringType.GetMethod("Contains", new Type[] { stringType });

  foreach (var constraint in constraints)
  {
    var equalsExpression = (Expression)Expression.Call(constraint.Key.Body, containsMethod, Expression.Constant(constraint.Value, stringType));
    expressions.Add(equalsExpression);
  }

  var body = expressions.Aggregate((accumulate, equal) => Expression.And(accumulate, equal));

  ParameterExpression p = constraints.First().Key.Parameters.First();
  return Expression.Lambda<Func<TElement, bool>>(body, p);
}

我想我在构建表达式树时做错了一些事情,因为我得到以下异常: 无效操作异常 - 参数“t”未绑定(bind)到指定的 LINQ to Entities 查询表达式中。

有谁知道如何解决这个问题?

最佳答案

您实际上真的很接近。问题是具有相同名称和类型的参数对象在技术上并不“相等”。

var b = Expression.Parameter(typeof(string), "p") == 
    Expression.Parameter(typeof(string), "p");
//b is false

因此,您创建的 lambda 的参数是您作为输入的第一个表达式的参数。在所有其他表达式的主体中使用的参数是不同的参数,并且它们没有作为参数提供给 lambda,所以错误就是因为这个。

解决方案实际上相当简单。您只需将所有其他参数的所有实例替换为您要使用的实际参数。

这是一个辅助方法(使用辅助类),它获取某个表达式中一个表达式的所有实例并将其替换为另一个:

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

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

现在我们只需在每个主体上调用一次,替换为一个公共(public)参数:

public static Expression<Func<TElement, bool>> BuildFilterExpression<TElement>(
    Dictionary<Expression<Func<TElement, string>>, string> constraints)
{
    List<Expression> expressions = new List<Expression>();

    var stringType = typeof(string);
    var containsMethod = stringType.GetMethod("Contains", new Type[] { stringType });

    var parameter = Expression.Parameter(typeof(TElement));

    foreach (var constraint in constraints)
    {
        var equalsExpression = (Expression)Expression.Call(
            constraint.Key.Body.Replace(constraint.Key.Parameters[0], parameter),
            containsMethod, Expression.Constant(constraint.Value, stringType));
        expressions.Add(equalsExpression);
    }

    var body = expressions.Aggregate((accumulate, equal) =>
        Expression.And(accumulate, equal));

    return Expression.Lambda<Func<TElement, bool>>(body, parameter);
}

关于c# - linq to entities dynamic where 从 lambdas 构建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19712337/

相关文章:

c# - libzmq.dll FileNotFoundException 异常

c# - 使用 oauth2 和 AngularJS 向 Web API 中的 token 发送附加参数

c# - XML 序列化错误 - 类型 'ItemsElementName' 的选择标识符 'ItemsChoiceType[]' 的值无效或缺失

entity-framework - 如何在 Visual Studio 2010 中手动编辑 ADO.NET 中的表映射?

c# - 如何格式化 NLog 异常输出以获得行分隔符?

c# - 不确定 linq to sql 查询的返回类型

c# - 如何将 foreach 循环转换为 Linq 查询?

c# - 使用 Linq 不等于

c# - Entity Framework 代码优先延迟加载

c# - 尽管定义了键,但 EntityType 没有键定义错误