У меня есть DbQuery, который я пытаюсь отфильтровать на основе различных (необязательных) параметров, предоставленных пользователем. Я пытаюсь составить запрос с помощью LINQ, чтобы учесть это, но натолкнулся на загвоздку, так что это как бы два участника.
Postgres: я использую postgres, поэтому у меня есть столбец массива, и я хочу иметь возможность использовать useCaseArray && entity.useCases. Однако поставщик EF в настоящее время не поддерживает это.
Я не хочу переходить к составлению всего этого в raw sql, если я могу избежать этого, поэтому я подумал, что могу сделать очень уродливый ГДЕ, как это:
WHERE (useCases.Contains(x) || useCases.Contains(y) ...)
однако я не знаю, как составить это с помощью LINQ. Я знаю, что вы можете сделать оперативные операции, такие как
query.Where(item => item.cases.Contains(x) || item.cases.Contains(y))
Однако я не могу написать это так, потому что мне нужно использовать foreach/loop для массива, содержащего мои X и Y. Кто-нибудь знает, как я могу это сделать?
foreach(var usecase in request.UseCases)
{
query = query.Where(item => item.UseCases.Contains(usecase));
}
Это сгенерирует только длинный ряд AND, когда я хочу, чтобы все это подмножество было одним OR.
Я надеюсь, что мне удалось объяснить это соответствующим образом! С другой стороны, я хотел бы иметь возможность вставлять один компонент предложения WHERE в сырой SQL, но я думаю, что это приведет к взрыву EF Core, и звучит так, как если бы FromSQL не поддерживал WHERE просто SELECT.
ОБНОВИТЬ:
На основе комментариев я попробовал это: https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/, который работает в основном фантастически:
var useCaseQuery = request.UseCases
.Select(useCase => PredicateBuilder.Create<MyEntity>(entity => entity.UseCases.Contains(useCase)))
.Aggregate(PredicateBuilder.Or);
query = query.Where(useCaseQuery);
В некотором смысле это здорово, но EF Core все еще не нравится:
The LINQ expression 'where ({[assumption].UseCases => Contains(__useCase_3)} OrElse {[assumption].UseCases => Contains(__useCase_4)})' could not be translated and will be evaluated locally.
Я думаю, что это будет хорошо для меня, но первоначальная проблема остается, я хотел бы, чтобы это работало на БД.
Поэтому после некоторых полезных комментариев я попробовал FromSql
if (request.UseCases != null && request.UseCases.Count > 0)
{
// I know UseCases is a List<int> hence why I'm just joining without escaping.
query = query.FromSql("SELECT * FROM my_table WHERE ARRAY[" + String.Join(',', request.UseCases) + "] && use_cases");
}
Мне это не нравится на 100%, потому что теперь мой сервис знает о моих таблицах базы данных и именах полей, что в значительной степени сводит на нет цель ORM. Однако для этого конкретного случая он делает то, что мне нужно, и я могу продолжать сочинять так:
if (request.Groups != null && request.Groups.Count > 0)
{
query = query.Where(data => request.Groups.Contains(data.GroupId));
}
if (!String.IsNullOrWhiteSpace(request.Title))
{
query = query.Where(data => EF.Functions.ILike(data.Name, $"%{request.Title}%"));
}
SQL производится так:
SELECT my_table.classes, my_table."group", my_table.group_id, my_table.id, my_table.name, my_table.use_cases
FROM (
SELECT * FROM my_table WHERE ARRAY[7,4] && use_cases
) AS assumption
WHERE my_table.group_id IN (7) AND (my_table.name ILIKE @__Format_3 ESCAPE '' = TRUE)
ORDER BY my_table.name DESC
Пока драйвер Postgres EF Core не будет обновлен, это может быть лучшим, что я могу сделать.