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