Entity Framework фильтрует данные по строке sql
Я храню некоторые данные фильтра в своей таблице. Позвольте мне сделать это более ясным: я хочу хранить некоторые предложения where
и их значения в базе данных и использовать их, когда хочу получить данные из базы данных.
Например, рассмотрим таблицу people
(набор объектов) и некоторые фильтры в ней в другой таблице:
"age" , "> 70"
"gender" , "= male"
Теперь, когда я извлекаю данные из таблицы people
, я хочу, чтобы эти фильтры отфильтровывали мои данные.
Я знаю, что могу сгенерировать SQL-запрос в виде строки и выполнить его, но есть ли другой лучший способ в EF, LINQ?
Ответы
Ответ 1
Одним из решений является использование Dynamic Linq Library, используя эту библиотеку, которую вы можете иметь:
filterTable = //some code to retrive it
var whereClause = string.Join(" AND ", filterTable.Select(x=> x.Left + x.Right));
var result = context.People.Where(whereClause).ToList();
Предположим, что в таблице фильтров есть столбцы Left
и Right
, и вы хотите присоединиться к фильтрам с помощью AND
.
Мое предложение состоит в том, чтобы включить более подробную информацию в таблицу фильтров, например, отделить операторы от операндов и добавить столбец, определяющий объединение, AND
или OR
и столбец, который определяет другую строку, которая объединяет этот, Вам нужна древовидная структура, если вы хотите обрабатывать более сложные запросы, такие как (A and B)Or(C and D)
.
Другое решение - построить дерево выражений из таблицы фильтров. Вот простой пример:
var arg = Expression.Parameter(typeof(People));
Expression whereClause;
for(var row in filterTable)
{
Expression rowClause;
var left = Expression.PropertyOrField(arg, row.PropertyName);
//here a type cast is needed for example
//var right = Expression.Constant(int.Parse(row.Right));
var right = Expression.Constant(row.Right, left.Member.MemberType);
switch(row.Operator)
{
case "=":
rowClause = Expression.Equal(left, right);
break;
case ">":
rowClause = Expression.GreaterThan(left, right);
break;
case ">=":
rowClause = Expression.GreaterThanOrEqual(left, right);
break;
}
if(whereClause == null)
{
whereClause = rowClause;
}
else
{
whereClause = Expression.AndAlso(whereClause, rowClause);
}
}
var lambda = Expression.Lambda<Func<People, bool>>(whereClause, arg);
context.People.Where(lambda);
Это очень упрощенный пример, вы должны сделать много типов кавычек типов валидации и..., чтобы это работало для всех типов запросов.
Ответ 2
Это интересный вопрос. Прежде всего, убедитесь, что вы честны с самим собой: вы создаете новый язык запросов, и это не тривиальная задача (как бы ни были тривиальны ваши выражения).
Если вы уверены, что не недооцениваете задачу, то вам захочется посмотреть на деревья выражений LINQ ( справочная документация).
К сожалению, это довольно широкий предмет, я призываю вас изучить основы и задать более конкретные вопросы по мере их появления. Ваша цель - интерпретировать ваши записи выражения фильтра (извлеченные из вашей таблицы) и создать дерево выражений LINQ для предиката, который они представляют. Затем вы можете передать дерево вызовам Where()
как обычно.
Ответ 3
Не зная, как выглядит ваш пользовательский интерфейс, вот простой пример того, о чем я говорил в своих комментариях относительно Serialize.Linq library
public void QuerySerializeDeserialize()
{
var exp = "(User.Age > 7 AND User.FirstName == \"Daniel\") OR User.Age < 10";
var user = Expression.Parameter(typeof (User), "User");
var parsExpression =
System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] {user}, null, exp);
//Convert the Expression to JSON
var query = e.ToJson();
//Deserialize JSON back to expression
var serializer = new ExpressionSerializer(new JsonSerializer());
var dExp = serializer.DeserializeText(query);
using (var context = new AppContext())
{
var set = context.Set<User>().Where((Expression<Func<User, bool>>) dExp);
}
}
Вероятно, вы можете получить привязанность к отражению и вызвать свой общий запрос LINQ на основе типов, входящих в выражение. Таким образом, вы можете избежать выражения выражения, как это было в конце примера.