gpt4 book ai didi

c# - 从字符串动态构建 LINQ 查询

转载 作者:太空宇宙 更新时间:2023-11-03 20:48:57 26 4
gpt4 key购买 nike

这是我的场景:

有一个对象集合,其中每个对象包含一个 Dictionary<string, string> .用户可以通过选择 Key 从另一个应用程序为该集合构建一组查询以获取子集。在Dictionary , 运算符,例如 >CONTAINS等,以及一个 Value .他们还可以平衡括号以创建查询组并选择 AND/OR运算符来组合查询。

例如,假设我有一个 Car 的集合对象和 Dictionary包含 Make 的键, Model , 和 Year .

我的应用以字符串的形式获取这些查询,如下所示:

"((Make = Honda) AND (Model CONTAINS Civic)) || (Year >= 2015)"

这告诉我来自 Car 的集合我想要的汽车有 Dictionary 的元素<Make, Honda> 的键/值和 <Model, anything that contains "Civic">或者 <Year, greater than or equal to 2015>

所以,我将它们解析出来并放入一个 QueryClass 中包含 Key 的三个字符串字段, Operator , 和 Value .我还跟踪查询之间的运算符,以及它们是否在一组括号中。

目前,我必须遍历每个 QueryClass一个一个地执行查询,检查前一个运算符是什么,它是否属于一个组,等等,并一遍又一遍地组合集合,直到它结束。这很乏味,而且似乎是一种糟糕的做事方式。如果有一种方法可以动态构建这些 LINQ 查询或在此集合上执行 SQL 语句(这些是必不可少的),那就更好了。

这是我的查询类,我将解析后的字符串存储在:

class QueryClass
{
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }

public QueryClass(string pInput)
{
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null)
{
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
}

我的解析类很长,所以我不会发布整个内容,但它会返回一个 List<object>其中每个元素是:

  • A QueryClass
  • “与”或“或”
  • 另一个列表,表示它是一组用括号分组的查询,包含上述两个选项。

这是 List<object> 的示例我在解析一个字符串后得到:

enter image description here

然后我循环遍历每个元素,确定该值是 double 还是字符串,然后对我的集合执行 LINQ 语句。我正在检查运算符是“AND”还是“OR”(如果只是一个查询,则不是),它是否属于一个组,并适本地组合结果。

最佳答案

这是我将您的查询转换为 Func 的实现.因为我不确定你的集合中是什么类型,所以我创建了一个接口(interface)来表示具有 attributes 的对象。 Dictionary<string, string>并对其进行处理。

基本上我在QueryClass中添加了一个方法将其转换为 Expression .它使用帮助字典 string->lambda 构建适当的比较 Expression对于每个运算符(operator)。然后我添加了一个类来转换 List<object>进入Func<IItem,bool>适用于 LINQ Where过滤器。

public interface IItem {
Dictionary<string, string> attributes { get; set; }
}

class QueryClass {
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }

public QueryClass(string pInput) {
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null) {
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}

static MethodInfo getItemMI = typeof(Dictionary<string, string>).GetMethod("get_Item");
static Dictionary<string, Func<Expression, Expression, Expression>> opTypes = new Dictionary<string, Func<Expression, Expression, Expression>> {
{ "==", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.Equal, lhs, rhs) },
{ ">=", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, Expression.Call(lhs, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), rhs), Expression.Constant(0)) },
{ "CONTAINS", (Expression lhs, Expression rhs) => Expression.Call(lhs, typeof(String).GetMethod("Contains"), rhs) }
};
static MemberInfo attribMI = typeof(IItem).GetMember("attributes")[0];

public Expression AsExpression(ParameterExpression p) {
var dictField = Expression.MakeMemberAccess(p, attribMI);
var lhs = Expression.Call(dictField, getItemMI, Expression.Constant(FieldName));
var rhs = Expression.Constant(Value);

if (opTypes.TryGetValue(Operator, out var exprMakerFn))
return exprMakerFn(lhs, rhs);
else
throw new InvalidExpressionException($"Unrecognized operator {Operator}");
}
}

public class LinqBuilder {
static Type TItems = typeof(IItem);

static Expression BuildOneLINQ(object term, ParameterExpression parm) {
switch (term) {
case QueryClass qc: // d => d[qc.FieldName] qc.Operator qc.Value
return qc.AsExpression(parm);
case List<object> subQuery:
return BuildLINQ(subQuery, parm);
default:
throw new Exception();
}
}

static Expression BuildLINQ(List<object> query, ParameterExpression parm) {
Expression body = null;
for (int queryIndex = 0; queryIndex < query.Count; ++queryIndex) {
var term = query[queryIndex];
switch (term) {
case string op:
var rhs = BuildOneLINQ(query[++queryIndex], parm);
var eop = (op == "AND") ? ExpressionType.AndAlso : ExpressionType.OrElse;
body = Expression.MakeBinary(eop, body, rhs);
break;
default:
body = BuildOneLINQ(term, parm);
break;
}
}

return body;
}

public static Func<IItem, bool> BuildLINQ(List<object> query) {
var parm = Expression.Parameter(TItems, "i");
return Expression.Lambda<Func<IItem, bool>>(BuildLINQ(query, parm), parm).Compile();
}
}

有了这个之后,您可以传入 List<object>表达式,然后过滤您的集合。给出查询 qIItem 的集合s cs ,你可以这样做:

var ans = cs.Where(LinqBuilder.BuildLINQ(q));

关于c# - 从字符串动态构建 LINQ 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57499140/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com