Как сделать соединение или предложение в Linq?

Если вы добавляете условия «и» в запрос Linq, это легко сделать так:

var q = MyTable;
if (condition1)
  q = q.Where(t => t.Field1 == value1);
if (condition2)
  q = q.Where(t => t.Field2 > t.Field3);
// etc.

Есть ли какой-нибудь умный способ сделать то же самое, когда вы хотите добавить условия «или»?


person Shaul Behr    schedule 19.06.2011    source источник
comment
Это включает в себя объединение деревьев выражений и (если вы используете EF) переписывание... Это не обязательно красиво... Но это можно сделать, если вам действительно нужно...   -  person Marc Gravell    schedule 19.06.2011
comment
@Marc Gravell - хммм ... определить некрасиво? У меня есть потребность в объединении предложений or , но если для создания элегантного решения потребуется больше усилий, чем, скажем, для создания отдельного запроса на объединение, то это может не стоить усилий. Насколько сложно это сделать?   -  person Shaul Behr    schedule 19.06.2011
comment
проверьте этот ответ stackoverflow.com/questions/ 6383825/   -  person Eranga    schedule 19.06.2011
comment
Эй, @Eranga, почему бы не опубликовать это решение в качестве ответа здесь?   -  person Shaul Behr    schedule 19.06.2011
comment
ответьте другим решением, чтобы вы могли выбрать любой метод, который вам нравится   -  person Eranga    schedule 19.06.2011


Ответы (5)


Вы можете использовать PredicateBuilder и использовать его для построения выражения на основе Or:

 var predicate = PredicateBuilder.False<Product>();

 predicate = predicate.Or (t => t.Field1 == value1);
 predicate = predicate.Or (t => t.Field2 > t.Field3);

 q = q.Where (predicate);

Подробнее об этом можно прочитать здесь: http://www.albahari.com/nutshell/predicatebuilder.aspx

Замените Product в PredicateBuilder.False<Product>() запрошенным объектом.

Обратите внимание, что вы начинаете с предиката False, так как хотите использовать Or. Если вам нужен предикат And, Юо должен начать с True

person Variant    schedule 19.06.2011
comment
Потрясающее решение. Спасибо! - person Shaul Behr; 19.06.2011

Используйте следующее:

var q = MyTable;
q = q.Where(
     t => (condition1 && t.Field1 == value1) || (condition2 && t.Field2 > t.Field3));
person Akram Shahda    schedule 19.06.2011
comment
Спасибо за предложение помощи! Приятно иметь некоторую добрую волю, пересекающую наши международные границы для разнообразия... ;) - person Shaul Behr; 19.06.2011

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

static void Main(string[] args)
{
    var source = new List<int> { 1, 2, 3 };

    var any = new List<Expression<Func<int, bool>>>();

    any.Add(x => x == 1);
    any.Add(x => x == 3);

    foreach (var item in source.AsQueryable().WhereDisjunction(any))
    {
        Console.WriteLine(item);
    }
}

class RewriteSingleParameterUsage : ExpressionVisitor
{
    public ParameterExpression Parameter { get; set; }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Parameter;
    }
}

public static IQueryable<T> WhereDisjunction<T>(this IQueryable<T> source, IList<Expression<Func<T, bool>>> any)
{
    switch (any.Count)
    {
        case 0: return source;
        case 1: return source.Where(any[0]);
        default:
            var p = Expression.Parameter(any[0].Parameters[0].Type, any[0].Parameters[0].Name);
            var rw = new RewriteSingleParameterUsage { Parameter = p };
            var expr = rw.Visit(any[0].Body);
            for (int i = 1; i < any.Count; i++)
            {
                expr = Expression.Or(expr, rw.Visit(any[i].Body));
            }
            return source.Where(Expression.Lambda<Func<T, bool>>(expr, p));
    }
}

В приведенном выше примере я очень резок, я эффективно заменяю любой параметр этим единственным новым параметром, который используется для создания нового выражения. Однако, учитывая сигнатуру этого метода расширения, на самом деле не должно быть возможности вызвать этот метод с такими параметрами, которые вызовут ошибку. Однако это будет проблемой, если вы задействуете более одного параметра.

person John Leidegren    schedule 19.06.2011
comment
Вы где-то ошиблись в этой булевой логике. Вы имеете в виду, что после condition2 должно быть ||? Тем не менее, это выглядит неправильно: если и условие1, и условие2 истинны, то вы создали условие и между двумя другими выражениями. Логическое выражение @ Akram является правильным ... - person Shaul Behr; 19.06.2011
comment
Я предупреждал вас, что это неинтуитивно. Теперь я вижу, что совершил ошибку, не говоря уже об этом тогда. - person John Leidegren; 19.06.2011
comment
Я дам вам +1 за усилия, но я думаю, что PredicateBuilder — лучшее и простое решение, предлагаемое здесь. Спасибо, в любом случае! - person Shaul Behr; 19.06.2011
comment
@Shaul - хотя библиотека, которая делает что-то за вас, иногда хороша, я думаю, что также необходимо понимать, как эти вещи работают. PredicateBuilder делает больше, чем создает для вас составную дизъюнктуру, она также требует дополнительной ерунды, такой как операция AsExpandable. Эти вещи не бесплатны. - person John Leidegren; 19.06.2011

Это тот же ответ, который я дал здесь

Как сказал Марк Гравелл, это включает в себя объединение деревьев выражений.

Эта статья показывает, как это сделать. Первоначальная настройка требует немного усилий. Но оно того стоит.

Альтернативным решением является использование Predicate Builder. Статья не очень хорошо объясняет, что на самом деле происходит под капотом. Но в приведенной выше статье это хорошо объясняется.

person Eranga    schedule 19.06.2011

person    schedule
comment
Ну, на самом деле это не LINQ, поскольку он не основан на IQueryable или деревьях выражений, поэтому его нельзя перевести в другие среды выполнения. - person John Leidegren; 19.06.2011
comment
Да, правда, он не транслируется в SQL, что делает его довольно неэффективным, по крайней мере, для моих целей. - person Shaul Behr; 19.06.2011