#c# #entity-framework
#c# #entity-framework
Вопрос:
У меня есть следующие операторы EF в SQL и «filterBy», который представляет собой список с 1 элементом. первый выдает список из 1, как и ожидалось, а второй — нет. Я думал, что созданный sql функционально равен.
var adList = dbCtx.AttackDetails
.Where(d => filterBy == null || filterBy.Contains(d.Name))
.Where(d => d.StartedAt != null)
.Where(d => isCompleted ? (d.Status != 5) : (d.Status == 1))
.Include(t => t.StatusNavigation)
.ToList();
Если я напишу код следующим образом, то он вернет несколько элементов, как если бы предложение where «DbQuery.Где(d => filterBy.Содержит(d.Name ))»не выполнил. Я думал, что они оба функционально равны.
var dbQuery = dbCtx.AttackDetails;
if (filterBy != null)
{
dbQuery.Where(d => filterBy.Contains(d.Name));
}
dbQuery.Where(d => d.StartedAt != null)
.Where(d => isCompleted ? (d.Status != 5) : (d.Status == 1))
.Include(t => t.StatusNavigation);
var adList = dbQuery.ToList();
Комментарии:
1. Изменить
dbCtx.AttackDetails.AsQueryable()
и внутри, если изменить наdbQuery = dbQuery.Where....
Ответ №1:
Вы не сохраняете параметры своих .Where
запросов. Попробуйте что-то вроде:
var dbQuery = dbCtx.AttackDetails.AsQueryable();
if (filterBy != null)
{
dbQuery = dbQuery.Where(d => filterBy.Contains(d.Name));
}
По сути, вы хотите создавать an IQueryable
до тех пор, пока не получите все необходимые условия, а затем .ToList()
, когда вы будете готовы фактически отобразить результаты.
Комментарии:
1. Вы не можете назначить iqueryable для dbset
2. Вам просто нужно ввести DbQuery as
IQueryable<AttackDetail>
вместо usingvar
.3. Не я, а OP … я знаю это
Ответ №2:
Ваш первый пример — построение одного выражения.
Ваш второй пример не переназначает IQueryable
переменную, поэтому Where
предложения фактически ничего не делают. Он вернет все строки, потому что:
var dbQuery = dbCtx.AttackDetails;
// dbQuery is never re-assigned so...
var adList = dbQuery.ToList(); // returns all AttackDetails.
Простое исправление, как указал Джонатан:
var dbQuery = dbCtx.AttackDetails.AsQueryable(); // Cast as IQueryable<AttackDetail>
if (filterBy != null)
{
dbQuery = dbQuery.Where(d => filterBy.Contains(d.Name));
}
dbQuery = dbQuery.Where(d => d.StartedAt != null)
.Where(d => isCompleted ? (d.Status != 5) : (d.Status == 1))
.Include(t => t.StatusNavigation);
var adList = dbQuery.ToList();
В качестве альтернативы, поскольку Where
все предложения объединены вместе, вы можете сгруппировать все обязательные фильтры в начале и впоследствии добавить любые необязательные:
var dbQuery = dbCtx.AttackDetails
.Include(t => t.StatusNavigation);
.Where(d => d.StartedAt != null)
.Where(d => isCompleted ? (d.Status != 5) : (d.Status == 1));
if (filterBy != null)
dbQuery = dbQuery.Where(d => filterBy.Contains(d.Name));
var adList = dbQuery.ToList();
Это просто помогает сделать код немного более компактным.
Мне любопытно, что это выражение: .Where(d => isCompleted ? (d.Status != 5) : (d.Status == 1));
фактически преобразуется в SQL. Несколько аккуратно, если это так, обычно это то, что я бы выделил в отдельное выражение if / else.
Ответ №3:
Суть вашей проблемы заключается в вашем селекторе, т.Е. filterBy.Contains(d.Name)
. Прежде чем мы перейдем к коду, давайте использовать здравый смысл. Должен ли данный объект отображаться в результатах поиска, когда задан данный фильтр?
ENTITY FILTER RETURN?
============================
ALAN | ALAN | YES
AL | AL | YES
ALAN | AL | YES <----
AL | ALAN | NO <----
Первые два очевидны, но давайте посмотрим на последние два. Когда имя содержит фильтр, оно должно быть возвращено. Когда фильтр содержит имя, его не следует возвращать (поскольку фильтр более строгий, чем имя объекта)
Теперь давайте вернемся к коду:
filterBy.Contains(d.Name)
С помощью этого кода вы бы вернули объект, если фильтр содержит имя. Если объект «AL», а фильтр «ALAN», приведенная выше логика фильтра вернет объект, а это неправильно.
Вот ссылка на скрипку, где вы можете поиграть с входными значениями и посмотреть, будет ли возвращен пример объекта или нет. Видя это в действии, он щелкает, больше, чем я объясняю это устно.
Чтобы устранить проблему, вам нужно инвертировать свой код:
d.Name.Contains(filterBy)
Для примера со скрипкой это было бы так entityName.Contains(filterBy)
.
Если вы заполните таблицу истинности для этой логики, вы увидите, что она в точности соответствует упражнению здравого смысла, которое мы выполняли в начале.