LINQ BuildContainsExpression с условиями OR - PullRequest
1 голос
/ 14 сентября 2010

Я пытаюсь заставить следующий запрос 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.IQueryable 1[Table1]' and 'System.Linq.IQueryable 1 [Table1]' ..

Есть идеи? -Я в правильном направлении?

Спасибо, Nir.

Ответы [ 2 ]

3 голосов
/ 14 сентября 2010

Вы добавляете свое ИЛИ не в том месте. То, что вы делаете сейчас, фактически выглядит примерно так:

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

Вторая проблема заключается в том, что используемый вами метод BuildContainsExpression возвращает лямбда-выражение, которое выглядит примерно так:

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

Вы не можете изменить это выражение, как только оно сгенерировано. Тем не менее, это то, что вы хотите, потому что вы хотели бы иметь что-то вроде этого:

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

Вы не можете просто взять тело этого лямбда-выражения и / или его вместе с другим выражением, потому что оно зависит от параметра 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.
}
1 голос
/ 26 апреля 2012

Я знаю, что я немного опоздал на вечеринку здесь - но я думаю, что оригинальный код в вопросе к оригинальному постеру был на 99% правильным.

Единственная ошибка была в том, что вместо

Expression.Or

это должно было быть

Expression.OrElse
...