Подтвердить что ты не робот

LINQ: различия между синглом Где с несколькими условиями и последовательными Wheres с одним условием

Есть ли недостаток в объединении нескольких Where в LINQ вместо использования одного Where с несколькими условиями?

Я спрашиваю, потому что использование нескольких Where может помочь значительно снизить сложность и улучшить ремонтопригодность моего кода.

Рассмотрим следующий код, chargeList - это List<Charge>, который является источником BindingSource:

IEnumerable<Charge> matchingCharges = chargeList;
if(!string.IsNullOrWhiteSpace(channelAbbr))
    matchingCharges = matchingCharges
        .Where(c => c.ChannelAbbreviation == channelAbbr);
if(deliveryNoteDate.HasValue)
    matchingCharges = matchingCharges
        .Where(c => c.ArrivalAt == deliveryNoteDate.Value);
if(chargeID.HasValue)
    matchingCharges = matchingCharges
        .Where(c => c.ChargeID == chargeID.Value);

Этот сжатый код будет обрабатывать все комбинации фильтров, ни одного, ни одного, ни одного.

В противном случае мне пришлось бы использовать if-else и несколько условий в одном Where.

Это лучшее, что приходит мне на ум:

// important to keep code readable:
bool filterChannel = !string.IsNullOrWhiteSpace(channelAbbr);
bool filterDate = deliveryNoteDate.HasValue;
bool filterID = chargeID.HasValue;

if(!filterChannel && !filterDate && !filterID)
{
    // take all 
    matchingCharges = chargeList;
}
else
{
    matchingCharges = chargeList
        .Where(c => 
            filterChannel ? c.ChannelAbbreviation == channelAbbr : true
            && filterDate ? c.ArrivalAt == deliveryNoteDate.Value : true
            && filterID   ? c.ChargeID ==  chargeID.Value : true);
}

Итак, каковы различия между ними, являются ли они незначительными? Имеет ли поставщик LINQ?

4b9b3361

Ответ 1

Семантически, нет разницы в случае Where (контраст OrderBy, который требует большей осторожности). На уровне реализации это просто несколько предикатов с простыми деревьями выражений вместо одного предиката со сложным деревом выражений; но большинство двигателей тоже справятся с этим.

Для того, что вы делаете, несколько Where идеальны.

Ответ 2

Мне было интересно то же самое. Вот почему я попробовал это в своем собственном приложении.

У меня есть список с большим количеством записей, и это то, что я пробовал:

//TEST 1
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var hoursLinq = _hourDataSource.Hours
            .Where(hour => hour.Id == profile.Id)
            .Where(hour => hour.DayName.Equals("Maandag"))
            .Where(hour => hour.Day == 1)
            .Select(hour => hour);
stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts1 = stopWatch.Elapsed;

//TEST 2
stopWatch = new Stopwatch();
stopWatch.Start();
var hoursLinq2 = _hourDataSource.Hours
            .Where(hour => hour.Id == profile.Id)
            .Select(hour => hour);

if (hoursLinq2.Count() != 0)
{
    var hoursLinq3 = _hourDataSource.Hours
            .Where(hour => hour.DayName.Equals("Maandag"))
            .Select(hour => hour);

    if (hoursLinq3.Count() != 0)
    {
        var hoursLinq4 = _hourDataSource.Hours
            .Where(hour => hour.Day == 1)
            .Select(hour => hour);
    }
}

stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts2 = stopWatch.Elapsed;

//TEST 3
stopWatch = new Stopwatch();
stopWatch.Start();
var hoursLinq5 = _hourDataSource.Hours
            .Where(hour => hour.Id == profile.Id &&
                            hour.DayName.Equals("Maandag") &&
                            hour.Day == 1)
            .Select(hour => hour);

stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts3 = stopWatch.Elapsed;

Каждый раз (ts1, ts2, ts3) имел такое маленькое различие в прошедшее время, что я уверен, что вы можете его игнорировать.

Я предполагаю, что это личное предпочтение, мне нравится множество, из-за которого читаемость