List <t>是表达式树中的常量,表达式参数还是其他什么?

时间:2017-05-30 13:47:53

标签: c# linq expression-trees

让我说我有这个lambda表达式,我想写一个表达式Tree on:

query.Where(d => (allCk && d.FacilityId == facilityId) //1.
      ||
     (!allCk && allSelected && d.FacilityId == facilityId  && !ids.Contains(d.Id)) //2.
      ||
     (!allCk && !allSelected && ids.Contains(d.Id))) //3.

这就是我设法写它的方式:为简洁起见,我只会显示第二个条件,这是所有(!allCk && allSelected && d.FacilityId == facilityId && !ids.Contains(d.Id))中最复杂的条件。

private Expression<Func<Documents, bool>> GetDocumentsPredicate(
        int facilityId, bool allCk, bool allSelected, List<int> ids)
{
  ParameterExpression pe = Expression.Parameter(typeof(Documents), "d");

  var listExpr1 = new List<Expression>();
  listExpr1.Add(Expression.IsFalse(Expression.Constant(allCk)));  //allCk
  listExpr1.Add(Expression.Constant(allSelected));                //allSelected

  var facilityParam = Expression.Constant(facilityId);            //facility
  Expression facilityIdProp = Expression.Property(pe, "FacilityId");
  Expression facilityIdEql = Expression.Equal(facilityIdProp, facilityParam);
  listExpr1.Add(facilityIdEql);

  //This is where I'm having trouble... Is ids a parameter or a constant???
  //Assuming it's a parameter...

  ParameterExpression peIds = Expression.Parameter(typeof(List<int>), "ids");
  Expression listContains = Expression.Call(
           pIds,
           typeof(List<int>).GetMethod("Contains"),
           Expression.Property(pe, "Id"));
  listExpr1.Add(Expression.Call(peIds, listContains, Expression.Property(pe, "Id"))); 
  var exp1 = listExpr1
             .Skip(1)
             .Aggregate(listExpr1[0], Expression.AndAlso);

  //...

}

我在此行收到错误:listExpr1.Add(Expression.Call(pIds, listContains, Expression.Property(pe, "Id"))); //无法转换&#39; Linq.Expression.ParameterExpression&#39; to&#39; System.Reflection.MethodInfo&#39;`。所以,它抱怨 pIds

那么这里的id是什么,常量,参数还是别的什么?

感谢您的帮助

修改

这就是我编写整个方法的方法

private Expression<Func<Documents, bool>> GetDocumentsPredicate(
    int facilityId, bool allCk, bool allSelected, List<int> ids)
{
  ParameterExpression pe = Expression.Parameter(typeof(Document), "d");
  Expression constIds = Expression.Constant(ids);
  Expression allCkParam = Expression.Constant(allCk);
  Expression allSelectedParam = Expression.Constant(allSelected);

  Expression listContains = Expression.Call(
           constIds,
           typeof(List<int>).GetMethod("Contains"),
           Expression.Property(pe, "Id"));

  /*(allCk && d.FacilityId == facilityId) ==> exp0*/
  var facilityParam = Expression.Constant(facilityId);


  Expression facilityIdProp = Expression.Property(pe, "FacilityId");
  Expression facilityIdEql = Expression.Equal(facilityIdProp, facilityParam);

  Expression exp0 = Expression.AndAlso(allCkParam, facilityIdEql);

/*(!allCk && allSelected && d.FacilityId == facilityId && !ids.Contains(d.Id))) 
  ==> exp1 */
  var listExpr1 = new List<Expression>();
  listExpr1.Add(Expression.IsFalse(allCkParam));
  listExpr1.Add(allSelectedParam);
  listExpr1.Add(facilityIdEql);
  listExpr1.Add(Expression.IsFalse(listContains));

  var exp1 = listExpr1
            .Skip(1)
            .Aggregate(listExpr1[0], Expression.AndAlso);


   /* (!allCk && !allSelected && ids.Contains(d.Id)) ==> exp2 */
   var listExpr2 = new List<Expression>();
   listExpr2.Add(Expression.IsFalse(allCkParam));
   listExpr2.Add(Expression.IsFalse(allSelectedParam));
   listExpr1.Add(listContains);

   var exp2 = listExpr2
            .Skip(1)
            .Aggregate(listExpr2[0], Expression.AndAlso);

   var listExpr = new List<Expression> { exp0, exp1, exp2 };
   var exp = listExpr
            .Skip(1)
            .Aggregate(listExpr[0], Expression.OrElse);

    var expr = 
              Expression.Lambda<Func<Document, bool>>(exp, 
              new ParameterExpression[] { pe });
    return expr;
 }

这是我将鼠标悬停在返回值

上时得到的结果
d => (((False AndAlso (d.FacilityId == 1)) 
     OrElse 
     (((IsFalse(False) AndAlso False) AndAlso (d.FacilityId == 1)) 
        AndAlso 
        IsFalse(value(System.Collections.Generic.List`1[System.Int32]).Contains(d.Id)))) 
     OrElse 
    ((IsFalse(False) AndAlso IsFalse(False)) 
        AndAlso 
        value(System.Collections.Generic.List`1[System.Int32]).Contains(d.Id)))

使用此语句var count = query.Count();进行简单测试会产生异常:Unknown LINQ expression of type 'IsFalse'

2 个答案:

答案 0 :(得分:5)

让我们总结一下。您在此次调用时遇到重载解析错误:

Expression.Call(peIds, listContains, Expression.Property(pe, "Id"))

peIds的类型为ParameterExpressionlistContains的类型为Expression。 第三个参数是MemberExpression类型。

现在,查看Expression.Call的重载列表。这些都不符合这三个论点。

编译器的最佳猜测是你试图调用这个:

https://msdn.microsoft.com/en-us/library/dd324092(v=vs.110).aspx

因此错误:第一个参数不是方法信息。

选择您要调用的重载,然后提供这些类型的参数。

但坦率地说,我不明白你为什么要这样做;我没有看到这个原始lambda的哪一部分意图代表。为什么这条线不仅仅是listExpr1.Add(listContains); ???

答案 1 :(得分:2)

就表达式而言,

ids不是参数,因为它不会传递给Func<...>。您需要匹配的id列表构建在表达式的结构中,因此Expression.Constant是处理它的正确方法。

然而,这不是全部故事:因为ids是一个引用类型,所以只有对它的引用才会进入表达式的结构。因此,如果您决定在构造表达式后更改列表的内容,ids.Contains的含义将会发生变化。

如果这不是您想要达到的效果,请复制一份清单:

Expression constIds = Expression.Constant(ids.ToList());