【问题标题】:linq to entities dynamic where build from lambdaslinq 到从 lambdas 构建的动态实体
【发布时间】:2013-10-31 16:51:59
【问题描述】:

我有一组这样的 lambdas

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

我想找到一种方法来构建一个表达式,该表达式可以在 Linq to Entities 中的 Where 语句中使用,其中这些 lambdas 与使用 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”未绑定在指定的 LINQ to Entities 查询表达式中。

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

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 非常感谢。这听起来是一个非常好的解决方案。不幸的是,我忘了提到我必须使用 .NET 3.5 - 正如我刚刚看到的 ExpressionVisitor 类是在 .NET 4.0 中引入的(或者到那时公开)。你知道我如何用 .NET 3.5 解决这个问题吗?
    • 幸运的是,我在 The Wayward WebLog - blogs.msdn.com/b/mattwar/archive/2007/07/31/… 中找到了 ExpressionVisitor 的代码。现在可以了!
    【解决方案2】:

    关闭。不幸的是,如果你看看你的每个属性 lambdas,例如..

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

    你会发现他们都是Expression.Property。然而,他们每个人都有不同的Expression.Parameter。您想使用ExpressionVisitorPropertyExpression.Parameter 替换为与Expression.Parameter 相同的实例,并将其与Expression.Lambda 一起使用。

    异常无效操作异常 - 参数 't' 未绑定在指定的 LINQ to Entities 查询表达式中。 表示您的 lambda 正文中有 ParameterExpressions 不在lambda 的参数数组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-10
      • 1970-01-01
      相关资源
      最近更新 更多