【问题标题】:LINQ BuildContainsExpression With OR conditions带有 OR 条件的 LINQ BuildContainsExpression
【发布时间】:2011-04-12 03:40:40
【问题描述】:

我正在尝试让以下 SQL 查询在 LINQ 中工作:

    Select id from table1 where id in (1,2) or canceledId in (1,2)

我正在使用 BuildContainsExpression 来实现“IN”条件,但我不知道如何实现“或”条件。 我在黑暗中的拍摄如下:

var identifiers = new List<int> {1,2};

var query = (from t in Context.Table1
             select t);

var query =
    query.Where(BuildContainsExpression<Table1, int>(t => t.Id, identifiers));

if (showCanceled)
{
   var expression = query.Where(BuildContainsExpression<Table1, int>(t => t.CanceledId.Value, identifiers)).Expression;
   Expression.Or(expression, transactionsQuery.Expression);
}

但我得到以下异常:
二元运算符 Or 没有为类型 'System.Linq.IQueryable1[Table1]' and 'System.Linq.IQueryable1[Table1]' 定义..

有什么想法吗? -我的方向正确吗?

谢谢, 尼尔。

【问题讨论】:

  • 您在使用 .NET 3.5 中的实体框架吗?
  • 是的,(3.5 SP1)很抱歉没有提到这一点。

标签: linq frameworks entity contains


【解决方案1】:

我知道我在这里聚会有点晚了 - 但我认为原始海报问题中的原始代码是 99% 正确的。

唯一的错误是,而不是

Expression.Or

应该是的

Expression.OrElse

【讨论】:

    【解决方案2】:

    您将 OR 附加在错误的位置。你现在所做的实际上是这样的:

    (from t in Context.Table1
     where identifiers.Contains(t.Id)
     select t)
    OR
    (where identifiers.Contains(t.CanceledId))
    

    第二个问题是你使用的BuildContainsExpression方法,返回一个lambda表达式,看起来像这样:

    t => t.Id == 1 || t.Id == 2 || ...
    

    此表达式一经生成便无法更改。然而,这正是你想要的,因为你想要这样的东西:

    t => t.Id == 1 || t.Id == 2 || ... || t.CanceledId == 1 || t.CanceledId == 2 || ...
    

    您不能简单地将这个 lambda 表达式的主体和/或它与另一个表达式一起使用,因为它取决于参数 t

    所以你可以做的是:

    // Overload of BuildContainsExpression.
    private static Expression<Func<T, bool>> BuildOtherContainsExpression<T>(
        ParameterExpression p, Expression field1, Expression field2, int[] values)
    {
        var eq1 = values.Select(v => Expression.Equal(field1, Expression.Constant(v)));
        var eq2 = values.Select(v => Expression.Equal(field2, Expression.Constant(v)));
    
        var body = eq1.Aggregate((acc, equal) => Expression.Or(acc, equal));
        body = eq2.Aggregate(body, (acc, equal) => Expression.Or(acc, equal));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }
    
    // Create a parameter expression that represents something of type Table1.
    var parameter = Expression.Parameter(typeof(Table1), "t");
    
    // Create two field expressions that refer to a field of the parameter.
    var idField = Expression.Property(parameter, "Id");
    var canceledIdField = Expression.Property(parameter, "CanceledId");
    
    // And finally the call to this method.
    query.Where(BuildContainsExpression<Table1>(
        parameter, idField, canceledIdField, identifiers));
    

    你的 if 语句现在看起来像这样:

    if (!showCanceled)
    {
        // Use original version of BuildContainsExpression.
    }
    else
    {
        // Create some expressions and use overloaded version of BuildContainsExpression.
    }
    

    【讨论】:

    • 我在赋值 (Expression.Or) 的内部遇到编译错误:无法将类型“System.Linq.Expressions.BinaryExpression”隐式转换为“System.Linq.Expressions.Expression>' 另外,.Expression 是不需要的,因为它已经是一个表达式,不是吗?
    • 你是对的,我回答你的问题有点太早了。现在正在研究解决方案..
    • 嗨,首先 - 感谢您花时间处理这个问题。当我尝试创建参数的字段时,我得到: System.ArgumentException: Field 'Id' is not defined for type Table1。当我使用类型反射检查属性时,我看到 3 个属性:名称、节点类型和类型。
    • 这是有道理的,我用一个包含字段的类进行了测试,但在现实世界中它是一个属性。我更新了我的答案。
    • 越来越近...现在我得到以下信息(因为 CanceledId 字段实际上可以为空): System.InvalidOperationException:二进制运算符 Equal 没有为类型“System.Nullable”1[System. Int32]' 和 'System.Int32'.. 我尝试使用“Expression.Convert(idField, typeof(int?))”(在某处看到),但没有任何成功......知道吗?
    猜你喜欢
    • 2014-11-24
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 2014-05-30
    • 1970-01-01
    相关资源
    最近更新 更多