开发者

LINQ BuildContainsExpression With OR conditions

I'm trying to get the following SQL query to work in LINQ:

    Select id f开发者_开发问答rom table1 where id in (1,2) or canceledId in (1,2)

I'm using BuildContainsExpression to achieve the "IN" condition, but I can't figure out how to implement the "or" condition. My shot in the dark is as follows:

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

But I get the following exception:

The binary operator Or is not defined for the types 'System.Linq.IQueryable1[Table1]' and 'System.Linq.IQueryable1[Table1]'..

Any ideas? -Am I in the right direction?

Thanks, Nir.


You are appending your OR in the wrong place. What you are doing now is effectively something like this:

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

The second problem is that the BuildContainsExpression method you use, returns a lambda expression, something that looks like this:

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

You can't change this expression once it's generated. However, that's what you want because you'd like to have something like this:

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

You can't simply take the body of this lambda expression and or it together with another expression because it depends on the parameter t.

So what you can do is the following:

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

Your if statement would now look like this:

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


I know I'm a bit late to the party here - but I think the original code in the original poster's question was 99% right.

The only wrong was that instead of

Expression.Or

it should have been

Expression.OrElse
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜