LINQ: добавление предложения where только тогда, когда значение не равно NULL - PullRequest
21 голосов
/ 26 апреля 2011

Я знаю, что типичный способ таков:

IQueryable query = from staff in dataContext.Staffs;
if(name1 != null)
{
     query = from staff in query where (staff.name == name1);
}

Однако из программы, которую мы переняли у других разработчиков, мы увидели код, подобный следующему:

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);

Если это нормальный оператор SQL, я бы определенно сказал, что второй - плохая практика. Потому что он добавляет бессмысленное предложение where в запрос, когда name1 равно нулю.

Но я новичок в LINQ, поэтому я не уверен, отличается ли LINQ?

Ответы [ 9 ]

17 голосов
/ 26 апреля 2011

вы можете написать это как

IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);

Таким образом, вторая часть вашего состояния не будет оцениваться, если ваше первое условие оценивается как ложное

Обновление: если вы пишете

IQueryable query = from staff in dataContext.Staffs;
    query = from staff in query where (name1 == null || staff.name == name1);

, а name1 равно null, вторая часть вашего условия не будет оцениваться, поскольку для условия требуется только одно условие для возврата true

plz, см. эту ссылку для более подробной информации

10 голосов
/ 26 апреля 2011

Часто подобные вещи легче писать, используя свободный синтаксис, а не синтаксис запроса.

, например

IQueryable query = dataContext.Staffs;
if(name1 != null)
{
     query = query.Where(x => x.name == name1);
}

Так что, если name1 равно нулю, вы просто не делаете Where() вызов. Если у вас есть несколько разных фильтров, каждый из которых может или не может потребоваться, и, возможно, различные разные порядки сортировки, я считаю, что это становится намного более управляемым.

Редактировать для alex: Хорошо, я отвечал на вопрос о добавлении предложения where только тогда, когда значение не равно нулю. В ответ на другую часть вопроса я попробовал это с Entity Framework 4, чтобы увидеть, какой SQL генерирует LINQ. Вы делаете это путем приведения query к ObjectQuery и вызова .ToTraceString(). В результате предложение WHERE получилось следующим образом:

WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1

Так что, да, это классический плохой SQL, если у вас есть индекс в столбце name, не ожидайте его использования.

Edit # 2: Попробовал еще раз, используя LINQ to SQL, а не Entity Framework, с довольно разными результатами. На этот раз попытка запроса со значением name1, равным нулю, вообще не приводит к предложению WHERE, как вы надеетесь; попытка сделать это с name1, являющимся "a", привела к простым WHERE [t0].[name] = @p0 и @p0, отправленным как "a". Entity Framework, похоже, оптимизирует , а не . Это немного беспокоит.

3 голосов
/ 11 августа 2016

Лучший способ сделать это - создать себе метод расширения , который будет принимать условное выражение и выражение where. Если условие истинно, то оно будет использовать выражение where, иначе оно не будет его использовать. Это может значительно очистить ваш код, устраняя необходимость в операторах if.

public static class LinqExtensions
{
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}

Теперь вы можете написать свой код так:

IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
1 голос
/ 24 ноября 2016

Итак, я попробовал метод расширения .Where(..., x => ...), указанный здесь в качестве ответа, но он не работает с Entity Framework, так как Linq To Entities не знает, как его перевести на TSQL.

Итак, вот мое решение, включающее мой Func:

Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue))
{
    columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue;
}

_context.SomeEfPocos.Where(x => ..... &&
            ..... &&
            ..... &&)
.Where(columnBeingFilteredPredicate);

someColumnBeingFilteredValue в моем случае это строковый параметр в методе инкапсуляции со значением по умолчанию NULL.

1 голос
/ 15 декабря 2015

Мне нравится использовать выражение, например

    Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true;

    if (DateBirth.HasValue)
                {
                    Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth);
                    expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate);
                }

IQueryable query = dataContext.Persons;
 query = query.Where(expresionFinal);
1 голос
/ 26 апреля 2011

Я видел этот шаблон в стандартном SQL, и он кажется полезным, если у вас есть несколько параметров, которые могут быть NULL. Например:

SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
                       AND ( @LastName IS NULL OR LastName = @LastName )

Если вы видите это в LINQ, возможно, они просто слепо перевели свои старые SQL-запросы.

1 голос
/ 26 апреля 2011

LINQ отличается по ряду других причин (не по этим причинам), LINQ - это способ получить данные «более быстрым способом» с помощью небольшого кода и максимально возможной очистки кода, есть много преимуществ LINQ:

  1. Упрощает преобразование данных в объекты.Я уверен, что вы слышали, что термин «несоответствие импедансов» используется довольно часто, а это означает, что LINQ сокращает объем работы, которую вы должны выполнить для преобразования между объектно-ориентированным кодом и парадигмами данных, такими как иерархические, плоские файлы, сообщения,реляционные и многое другое.Это не устраняет «Несоответствие импеданса», потому что вы все равно должны рассуждать о своих данных в их исходной форме, но мост отсюда туда (IMO) намного короче.

  2. Aобщий синтаксис для всех данных.Как только вы изучите синтаксис запроса, вы можете использовать его с любым поставщиком LINQ.Я думаю, что это гораздо лучшая парадигма развития, чем Вавилонская башня, которая выросла за годы с технологиями доступа к данным.Конечно, у каждого поставщика LINQ есть уникальные нюансы, которые необходимы, но базовый подход и синтаксис запроса одинаковы.

  3. Строго типизированный код.Синтаксис запроса C # (или VB.NET) является частью языка, и вы кодируете с помощью типов C #, которые переводятся в то, что понимает поставщик.Это означает, что вы получаете производительность, когда ваш компилятор обнаруживает ошибки на более ранних этапах жизненного цикла разработки, чем где-либо еще.Конечно, многие ошибки в синтаксисе хранимых процедур вызывают ошибки при сохранении, но LINQ является более общим, чем SQL Server.Вы должны подумать обо всех других типах источников данных, которые генерируют ошибки времени выполнения, потому что их запросы формируются с помощью строк или какого-либо другого свободно типизированного механизма.

  4. Интеграция провайдера.Собрать источники данных очень легко.Например, вы можете использовать LINQ to Objects, LINQ to SQL и LINQ to XML вместе для некоторых очень сложных сценариев.Я думаю, что это очень элегантно.

  5. Сокращение в работе.До LINQ я потратил много времени на создание DAL, но теперь мой DataContext - это DAL.Я также использовал OPF, но теперь у меня есть LINQ, который поставляется с несколькими поставщиками в комплекте и многими другими сторонними поставщиками, что дает мне преимущества от моих предыдущих пунктов.Я могу настроить LINQ to SQL DataContext за минуту (так быстро, как мой компьютер и IDE могут поддерживать скорость).

  6. Производительность в общем случае не становится проблемой.В наши дни SQL Server довольно хорошо оптимизирует запросы, как и хранимые процедуры.Конечно, все еще есть случаи, когда хранимые процедуры необходимы по соображениям производительности.Например, я нашел более разумным использовать хранимый процесс, когда у меня было несколько взаимодействий между таблицами с дополнительной логикой внутри транзакции.Затраты на связь при попытке выполнить ту же задачу в коде в дополнение к вовлечению кода DTC в распределенную транзакцию сделали выбор хранимого процесса более убедительным.Однако для запроса, который выполняется в одном операторе, LINQ - мой предпочтительный выбор, потому что даже если бы был небольшой выигрыш в производительности от хранимого процесса, преимущества в предыдущих точках (IMO) имеют больший вес.

  7. Встроенная охрана.Одной из причин, по которой я предпочел хранимые процедуры до LINQ, было то, что они принудительно использовали параметры, помогая уменьшить атаки с использованием SQL-инъекций.LINQ to SQL уже параметризует ввод, который так же безопасен.

  8. LINQ является декларативным.Большое внимание уделяется работе с LINQ to XML или LINQ to SQL, но LINQ to Objects невероятно мощный.Типичным примером LINQ to Objects является чтение элементов из строки [].Однако это всего лишь маленький пример.Если вы думаете обо всех коллекциях IEnumerable (вы также можете запросить IEnumerable), с которыми вы работаете каждый день, возможностей у вас много.т. е. поиск в элементе управления ASP.NET ListBox для выбранных элементов, выполнение операций над наборами (например, Union) в двух коллекциях или итерация по списку и запуск лямбда-выражения в ForEach каждого элемента.Как только вы начнете думать в LINQ, который носит декларативный характер, вы обнаружите, что многие из ваших задач проще и интуитивнее, чем императивные методы, которые вы используете сегодня.

Я мог бы, вероятно,продолжай, но я бы лучше на этом остановился.Надеемся, что это даст более позитивное представление о том, как вы могли бы быть более продуктивным с LINQ, и, возможно, посчитаете его полезной технологией в более широкой перспективе.

0 голосов
/ 25 июля 2017

Для EF Core я разбил это так:

IQueryable<Partners> recs = contextApi.Partners;
if (status != -1)
{
   recs = recs.Where(i => i.Status == status);
}
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName);
foreach (var rec in recs)
{
}

Я должен был быть явным при наборе текста вместо того, чтобы полагаться на var.

0 голосов
/ 26 апреля 2011

Нет, я не совсем согласен с вами. здесь вы просто дали простую логику

if(name1 != null)
// do your stuff

но что произойдет, если вы сделаете что-то другое с именем1, которое имеет нулевое значение .. !! Хорошо, теперь рассмотрим эту ситуацию. В этом примере показано, как обрабатывать возможные нулевые значения в исходных коллекциях. Коллекция объектов, такая как IEnumerable<T>, может содержать элементы с нулевым значением. Если исходная коллекция равна нулю или содержит элемент, значение которого равно нулю, и ваш запрос не обрабатывает нулевые значения, NullReferenceException будет выдано при выполнении запроса.

Возможно, это может быть проблемой ...

...