Entity Framework фильтрует данные по строке sql

Я храню некоторые данные фильтра в своей таблице. Позвольте мне пояснить: я хочу сохранить некоторые предложения where и их значения в базе данных и использовать их, когда я хочу получить данные из базы данных.

Например, рассмотрим таблицу people (набор сущностей) и некоторые фильтры для нее в другой таблице:

"age" , "> 70"
"gender" , "= male"

Теперь, когда я извлекаю данные из таблицы people, я хочу, чтобы эти фильтры фильтровали мои данные.

Я знаю, что могу сгенерировать SQL-запрос в виде строки и выполнить его, но есть ли другой лучший способ в EF, LINQ?


person ConductedClever    schedule 21.08.2015    source источник
comment
В структуре сущностей вы можете использовать функцию Where следующим образом: ob.Where(p =› p.age › 70 && gender == male).   -  person MaticDiba    schedule 21.08.2015
comment
возраст и 70 — это строковые фильтры, которые извлекаются из БД. так как я могу написать эту строку кода? Прочитайте вопрос еще раз, пожалуйста. Спасибо.   -  person ConductedClever    schedule 21.08.2015
comment
Вы можете использовать этот построитель выражений.   -  person Sirwan Afifi    schedule 21.08.2015
comment
Что-то подобное внутри?   -  person ConductedClever    schedule 21.08.2015
comment
Просто деталь: я бы по крайней мере поместил оператора в отдельное поле   -  person Raphaël Althaus    schedule 21.08.2015
comment
@ RaphaëlAlthaus Я согласен с тобой. Мне нужно хотя бы переписать этот конструктор, чтобы получить оператор в дополнение к фильтру и значению. Любое другое внутреннее решение?   -  person ConductedClever    schedule 21.08.2015
comment
Почему вы вообще хотите разделить предложение where? Если вы разделитесь, то реконструкция, по крайней мере, потенциально может стать головной болью.   -  person Paul Zahra    schedule 21.08.2015
comment
@PaulZahra Это может упростить синтаксический анализ (или, скорее, избежать необходимости синтаксического анализа в некоторой степени, поскольку предикат уже структурирован). Обратите внимание, что в конечном итоге вы хотите построить деревья выражений LINQ, а не строки. T.Rahgooy предлагает использовать Dynamic LINQ для анализа произвольного языка, и это определенно неплохая идея, хотя, если OP динамически строит предикаты (а не только некоторые значения), тогда OP придется прибегнуть к некоторой конкатенации строк (обычно не рекомендуется) или более продвинутый codegen (который запутан, учитывая, что вы можете просто использовать первоклассные объекты для построения деревьев выражений).   -  person tne    schedule 21.08.2015
comment
@ConductedClever Если вы хотите, чтобы ваш вопрос был более общим/точным, вам следует перефразировать его примерно так: «Как я могу фильтровать данные, используя динамически создаваемые предикаты, полученные из таблицы базы данных?». Но тогда, возможно, ваша инстинктивная формулировка будет такой же, как и у других людей, что облегчит поиск. Ваш звонок.   -  person tne    schedule 21.08.2015
comment
Пожалуйста, добавьте C# и expression-trees в теги вашего вопроса   -  person Taher Rahgooy    schedule 21.08.2015
comment
@T.Rahgooy Хорошая идея, пометил linq-expressions, чтобы быть более конкретным (может быть, и dynamic-linq тоже). Не уверен насчет С#, ОП не указал.   -  person tne    schedule 21.08.2015
comment
@T.Rahgooy, ты великолепен, и твой ответ был лучше. Я работаю над таблицей фильтров и деревом выражений, чтобы быть более гибкими. Я хочу принять ваш ответ, но я хочу опубликовать завершенный ответ после достижения желаемого результата самостоятельно. Тогда я приму твое. большое спасибо. Я хочу, чтобы БОГ дал тебе все, что ты пожелаешь.   -  person ConductedClever    schedule 21.08.2015
comment
@tne Как я понял, ты такой умный и такой умный. ваше видение моего вопроса было даже лучше моего! большое спасибо за вашу помощь.   -  person ConductedClever    schedule 21.08.2015
comment
Другая мысль может состоять в том, чтобы сериализовать/десериализовать ваши деревья выражений и сохранить их в вашей БД. Я сделал что-то подобное, и это работает очень хорошо. Посмотрите здесь. Затем вы можете передать выражение в предложение LINQ Where.   -  person DDiVita    schedule 21.08.2015
comment
Я также использовал библиотеку Serialize.Linq, которая позволяет выполнять сериализацию в различные форматы.   -  person DDiVita    schedule 21.08.2015
comment
@DDiVita В зависимости от того, насколько легко вы можете разбивать и составлять подвыражения в сериализованной форме (я предполагаю, что очень легко), это звучит как отличный выбор, поскольку позволяет OP отказаться от создания нового языка с помощью определенного генератора кода и синтаксический анализатор. Я предполагаю, что эти фильтры генерируются динамически из какого-то пользовательского интерфейса; пользовательский интерфейс может просто создавать деревья выражений LINQ и сериализовать каждый узел отдельно, чтобы вставить его в таблицу с иерархической структурой. Вы должны опубликовать это как ответ.   -  person tne    schedule 21.08.2015


Ответы (3)


Одним из решений является использование динамической библиотеки Linq. С помощью этой библиотеки вы можете имеют:

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);

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

person Taher Rahgooy    schedule 21.08.2015
comment
Ваш пример очень поможет ОП визуализировать то, что нужно сделать. Позвольте мне предложить заинтересованным читателям кое-что, чтобы они могли определить свой следующий шаг: рассмотрите шаблон интерпретатора или шаблон посетителя (сравнение). Обычно это приводит к тому, что дизайн легче поддерживать, но идея, конечно, та же. - person tne; 21.08.2015
comment
Оба ваших ответа блестящие. но я думаю, что мне нравится второй больше. Большое спасибо. - person ConductedClever; 21.08.2015
comment
Пожалуйста. Второй более сложный, я поздравляю вас с вашим мужеством!! - person Taher Rahgooy; 21.08.2015

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

Если вы уверены, что не недооцениваете задачу, посмотрите на Деревья выражений LINQ (справочная документация).

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

person tne    schedule 21.08.2015

Не зная, как выглядит ваш пользовательский интерфейс, вот простой пример того, о чем я говорил в своих комментариях относительно Serialize.Linq. библиотека

    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 на основе типов, поступающих из выражения. Таким образом, вы можете избежать приведения выражения, как я сделал в конце примера.

person DDiVita    schedule 22.08.2015