Entity Framework 按字符串过滤数据 sql
Entity Framework filter data by string sql
我正在 table 中存储一些过滤数据。让我说得更清楚:我想将一些 where
子句及其值存储在数据库中,并在我想从数据库中检索数据时使用它们。
例如,考虑一个 people
table(实体集)和另一个 table 中的一些过滤器:
"age" , "> 70"
"gender" , "= male"
现在,当我从 people
table 检索数据时,我想使用这些过滤器来过滤我的数据。
我知道我可以生成一个 SQL 查询作为字符串并执行它,但是在 EF、LINQ 中还有其他更好的方法吗?
这是一个有趣的问题。首先,确保您对自己诚实:您正在创建一种新的查询语言,这不是一项微不足道的任务(无论您的表达式看起来多么微不足道)。
如果您确定自己没有低估任务,那么您会想看看 LINQ expression trees (reference documentation)。
不幸的是,这是一个相当广泛的主题,我鼓励您学习基础知识并在出现问题时提出更具体的问题。您的目标是解释您的过滤器表达式记录(从您的 table 中获取)并为它们表示的谓词创建一个 LINQ 表达式树。然后您可以像往常一样将树传递给 Where()
调用。
一个解决方案是使用 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();
假设过滤器 table 有列 Left
和 Right
并且您想按 AND
.
加入过滤器
我的建议是在过滤器 table 中包含更多详细信息,例如将运算符与操作数分开并添加一个列来确定连接是 And
还是 OR
以及确定加入这一行的另一行的列。如果你想处理更复杂的查询,比如 (A and B)Or(C and D)
.
,你需要一个树结构
另一个解决方案是从过滤器 table 构建表达式树。这是一个简单的例子:
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);
这是一个非常简单的例子,你应该做很多验证类型转换......为了让它适用于所有类型的查询。
在不知道你的 UI 长什么样的情况下,这里是我在关于 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 查询,您可能会变得更有趣。这样您就可以避免像我在示例末尾那样强制转换表达式。
我正在 table 中存储一些过滤数据。让我说得更清楚:我想将一些 where
子句及其值存储在数据库中,并在我想从数据库中检索数据时使用它们。
例如,考虑一个 people
table(实体集)和另一个 table 中的一些过滤器:
"age" , "> 70"
"gender" , "= male"
现在,当我从 people
table 检索数据时,我想使用这些过滤器来过滤我的数据。
我知道我可以生成一个 SQL 查询作为字符串并执行它,但是在 EF、LINQ 中还有其他更好的方法吗?
这是一个有趣的问题。首先,确保您对自己诚实:您正在创建一种新的查询语言,这不是一项微不足道的任务(无论您的表达式看起来多么微不足道)。
如果您确定自己没有低估任务,那么您会想看看 LINQ expression trees (reference documentation)。
不幸的是,这是一个相当广泛的主题,我鼓励您学习基础知识并在出现问题时提出更具体的问题。您的目标是解释您的过滤器表达式记录(从您的 table 中获取)并为它们表示的谓词创建一个 LINQ 表达式树。然后您可以像往常一样将树传递给 Where()
调用。
一个解决方案是使用 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();
假设过滤器 table 有列 Left
和 Right
并且您想按 AND
.
我的建议是在过滤器 table 中包含更多详细信息,例如将运算符与操作数分开并添加一个列来确定连接是 And
还是 OR
以及确定加入这一行的另一行的列。如果你想处理更复杂的查询,比如 (A and B)Or(C and D)
.
另一个解决方案是从过滤器 table 构建表达式树。这是一个简单的例子:
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);
这是一个非常简单的例子,你应该做很多验证类型转换......为了让它适用于所有类型的查询。
在不知道你的 UI 长什么样的情况下,这里是我在关于 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 查询,您可能会变得更有趣。这样您就可以避免像我在示例末尾那样强制转换表达式。