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.IQueryable
1[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
精彩评论