Создание общей функции предиката
Во-первых, я не уверен, какие условия использовать, чтобы задать этот вопрос, что, вероятно, поэтому я не нашел ответа от себя.
Итак, я работаю с Linq to SQL (С#,.Net 4), и я хочу получить список всех пользователей, которые соответствуют критериям, основы которых я бы сделал примерно так:
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
но в этом случае есть несколько полей, которые я хочу сопоставить, все эти конкретные поля являются общей проверкой, и я хотел бы иметь возможность создавать функцию выделения, которую я могу использовать в любых моих пользовательских запросах проверьте этот матч.
Чтобы попытаться объяснить, что немного лучше дать пример: Допустим, у пользователя есть 5 флагов, и мне нужна общая проверка, чтобы увидеть, установлен ли какой-либо из этих флагов. Поэтому я мог бы написать свой запрос следующим образом:
var users = DataContext.Users.Where(x => x.Flag1 || x.Flag2 || x.Flag3 || x.Flag4 || x.Flag5);
Но то, что я хотел бы сделать, - это выделить "флажок флажка 5", чтобы я мог использовать его и в других запросах, в конечном итоге я хотел бы использовать что-то вроде:
var users = DataContext.Users.Where(x => x.Criteria1 == "something" && CheckForFlags(x));
Я пробовал это, имея такую функцию:
static bool CheckForFlags(User user)
{
return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}
но я получаю сообщение об ошибке:
"Метод" Boolean CheckForFlags (Пользователь) "не поддерживает перевод на SQL".
..., что имеет смысл, но это что-то, что я могу сделать, чтобы сделать эту работу так, как я ее хочу? Или это ограничение, потому что я использую Linq для SQL и на самом деле что-то, что будет работать с Linq для объектов?
Ответы
Ответ 1
Оптимальная вещь о том, как LINQ to SQL обрабатывает выражения, состоит в том, что вы можете фактически создавать выражения в другом месте вашего кода и ссылаться на них в своих запросах. Почему бы вам не попробовать что-то вроде этого:
public static class Predicates
{
public static Expression<Func<User, bool>> CheckForFlags()
{
return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
user.Flag4 || user.Flag5);
}
public static Expression<Func<User, bool>> CheckForCriteria(string value)
{
return (user => user.Criteria1 == value);
}
}
После определения ваших предикатов очень легко использовать их в запросе.
var users = DataContext.Users
.Where(Predicates.CheckForFlags())
.Where(Predicates.CheckForCriteria("something"));
Ответ 2
Вы пробовали PredicateBuilder? Я не использовал его более года, но я нашел его эффективным при написании запросов "Или где".
http://www.albahari.com/nutshell/predicatebuilder.aspx
Пример со своей страницы:
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}
Ответ 3
Я думаю, что это сработает, и я думаю, что я использовал его в своем проекте Linq-to-SQL, но я не могу найти пример без внимания. Если это не так, дайте мне знать.
Вместо создания функции создайте новое свойство объекта Users
:
partial class Users {
bool CheckForFlags
{
get {
return Flag1 || Flag2 || Flag3 || Flag4 || Flag5;
}
}
}
Затем вы должны иметь возможность делать
var users = DataContext.Users.Where(x => x.CheckForFlags);
Ответ 4
Насколько мне известно, существует два возможных способа сделать это.
Быстрый-простой способ фильтрации ваших результатов после выполнения SQL, с чем-то вроде этого:
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
.ToEnumerable()
.Where(x => CheckForFlags(x));
Однако это очень плохое с точки зрения производительности. Он вернет ВСЕ строки из базы данных, соответствующие только первым критериям, а затем отфильтрует результаты в памяти на клиенте. Функциональный, но далеко не оптимальный.
Вторая, намного более эффективная опция - создать UDF в самой базе данных и вызвать ее из LINQ. См. Например этот вопрос. Очевидным недостатком является то, что он перемещает код в базу данных, которую никто не любит делать (по многим допустимым причинам).
Там могут быть и другие жизнеспособные решения, но это единственные, о которых я знаю.
Ответ 5
Как вы сказали, это ограничение, потому что вы используете Linq для SQL.
Что-то вроде этого должно работать:
var users = DataContext.Users.Where(x => x.Criteria1 == "something")
.ToArray()
.Where(x => CheckForFlags(x));