Как сделать запрос по группам предикатов в EF Core - PullRequest
0 голосов
/ 13 мая 2019

Я хочу применить несколько составных фильтров к группе данных, подобной этой:

Filter[] filters = new[] { new Filter { Name = "Bob", Gender = "Male"  },
new Filter { Name = "Alice", Height = "Female" } };

_dbContext.People.Where(p => filter.Any(f => f.Name == p.Name && f.Gender == p.Gender)).Select(p => p.Id);

Меня интересует Ids из Мужских Бобов и Женские Алисы .Извините Женский Бобс .Я не хочу тебя.

Это правильный способ решения этой проблемы в памяти Linq, но есть проблема.Вот как выглядит SQL EF (я проверяю это в своем профилировщике SQL-сервера)

SELECT [p].[Name], [p].[Gender], [p].[Id] FROM [People] AS [p]

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

Есть ли способ сделать сгенерированный sql похожим на это?

SELECT 
[Person].[Id]
FROM [Person] 
WHERE 
   ((([Person].[Name] = "Bob") AND ([Person].[Gender] = "Male")) 
   OR (([Person].[Name] = "Alice") AND ([Person].[Gender] = "Female")))

(как это возможно в Dapper )

Ответы [ 3 ]

2 голосов
/ 13 мая 2019

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

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

1 голос
/ 13 мая 2019

Вот что я сделал в конце, как предложил @stuartd:

var predicate = PredicateBuilder.New<Person>();
foreach (var filter in filters)
{
    predicate = predicate.Or(p =>
       p.Gender == filter.Gender &&  filter.Name == p.Name));
}

people = _dbContext.People.Where(predicate).Select(r => r.Id).Distinct().ToArrayAsync();

Работает как шарм.Спасибо.

1 голос
/ 13 мая 2019

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

var maleNameFilter = "Bob";
var femaleNameFilter = "Alice";
_dbContext.People.Where(p => 
    (p.Name == maleNameFilter && p.Gender == "Male") 
    || (p.Name == femaleNameFilter && p.Gender == "Female")
).Select(p => p.Id);

Однако я подозреваю, что вы захотите использовать большое количество фильтров, и в этом случае он станет более жестким с LINQ. Как уже предлагалось в некоторых комментариях, вы можете использовать для этого Predicate Builder (см. пример ).

Наконец, если вам нужна максимальная производительность за счет немного большей сложности, вы можете подумать о том, чтобы поместить значения фильтра в отдельную таблицу в базе данных и переписать запрос, используя Join().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...