gpt4 book ai didi

c# - 列表上的动态查询选择包含列表

转载 作者:行者123 更新时间:2023-11-30 16:38:09 27 4
gpt4 key购买 nike

我正在尝试使用 C# 实现动态过滤机制。

我的用户类具有姓名、姓氏、出生日期等属性,还有 UserInformations 列表,它是 UserInformation 对象的列表。UserInformation 对象具有属性 Id、Name 和 Value。和用户列表中的 userList。

我使用 https://querybuilder.js.org/demo.html 创建了一个 UI 来创建过滤器页面

它生成像这样的过滤器

{
"condition": "AND",
"rules": [
{
"id": "name",
"field": "name",
"type": "string",
"input": "text",
"operator": "equal",
"value": "huseyin"
},
{
"condition": "OR",
"rules": [
{
"id": "surname",
"field": "surname",
"type": "string",
"input": "text",
"operator": "equal",
"value": "34"
},
{
"id": "ix_2_Weight",
"field": "ix_2_Weight",
"type": "integer",
"input": "select",
"operator": "equal",
"value": 50
}
]
}
],
"valid": true
}

我对具有用户属性的过滤器没有问题。但是,我正在尝试过滤 UserInformations 数组中的信息。如果它以 ix 开头并获取用户信息的 id,我将拆分字段名称。但我找不到如何比较它的值(value)。如果它是一个静态 linq,它应该像

users.Where(u=>u.Informations.FirstOrDefault(i=>i.id==2/*id of information in filter*/)?.Value=="something"))

使用以下代码调用过滤器

 private async Task<List<User>> FilterUsers(FilterRule rule, List<UserFilterDto> users = null)
{
List<User> list = users ?? await GetUsersForFilter();

var tempList = list.BuildQuery(rule).ToList();
return tempList;

}

动态查询部分如下。


public static IQueryable<User> BuildQuery(this IQueryable<User> queryable, FilterRule filterRule, out string parsedQuery, bool useIndexedProperty = false, string indexedPropertyName = null)
{
if (filterRule == null)
{
parsedQuery = "";
return queryable;
}

var pe = Expression.Parameter(typeof(UserDetailedDto), "item");

var expressionTree = BuildExpressionTree(pe, filterRule, useIndexedProperty, indexedPropertyName);
if (expressionTree == null)
{
parsedQuery = "";
return queryable;
}

parsedQuery = expressionTree.ToString();

var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<UserDetailedDto, bool>>(expressionTree, pe));

var filteredResults = queryable.Provider.CreateQuery<UserDetailedDto>(whereCallExpression);

return filteredResults;

}


public static IQueryable<User> BuildQuery(this IQueryable<User> queryable, FilterRule filterRule, out string parsedQuery, bool useIndexedProperty = false, string indexedPropertyName = null)
{
if (filterRule == null)
{
parsedQuery = "";
return queryable;
}

var pe = Expression.Parameter(typeof(UserDetailedDto), "item");

var expressionTree = BuildExpressionTree(pe, filterRule, useIndexedProperty, indexedPropertyName);
if (expressionTree == null)
{
parsedQuery = "";
return queryable;
}

parsedQuery = expressionTree.ToString();

var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<UserDetailedDto, bool>>(expressionTree, pe));

var filteredResults = queryable.Provider.CreateQuery<UserDetailedDto>(whereCallExpression);

return filteredResults;

}

private static Expression BuildExpressionTree(ParameterExpression pe, FilterRule rule, bool useIndexedProperty = false, string indexedPropertyName = null)
{

if (rule.Rules != null && rule.Rules.Any())
{
var expressions =
rule.Rules.Select(childRule => BuildExpressionTree(pe, childRule, useIndexedProperty, indexedPropertyName))
.Where(expression => expression != null)
.ToList();

var expressionTree = expressions.First();

var counter = 1;
while (counter < expressions.Count)
{
expressionTree = rule.Condition.ToLower() == "or"
? Expression.Or(expressionTree, expressions[counter])
: Expression.And(expressionTree, expressions[counter]);
counter++;
}

return expressionTree;
}
if (rule.Field != null)
{
Type type;

switch (rule.Type)
{
case "integer":
type = typeof(int);
break;
case "long":
type = typeof(long);
break;
case "double":
type = typeof(double);
break;
case "string":
type = typeof(string);
break;
case "date":
case "datetime":
type = typeof(DateTime);
break;
case "boolean":
type = typeof(bool);
break;
default:
throw new Exception($"Unexpected data type {rule.Type}");
}

Expression propertyExp = null;

if (rule.Field.StartsWith("ix"))
{
long informationId =long.Parse(rule.Field.Split("_")[1]);

????
????

} else if (useIndexedProperty)
{
propertyExp = Expression.Property(pe, indexedPropertyName, Expression.Constant(rule.Field));
}
else
{
propertyExp = Expression.Property(pe, rule.Field);
}

Expression expression;

if (propertyExp.Type.Name.Contains("ICollection") || propertyExp.Type.Name.Contains("List"))
{
// Rule Field is a Collection
expression = BuildCollectionExpression(pe, rule);
}
else
{

switch (rule.Operator.ToLower())
{
case "in":
expression = In(type, rule.Value, propertyExp);
break;
case "not_in":
expression = NotIn(type, rule.Value, propertyExp);
break;
case "equal":
expression = Equals(type, rule.Value, propertyExp);
break;
case "not_equal":
expression = NotEquals(type, rule.Value, propertyExp);
break;
case "between":
expression = Between(type, rule.Value, propertyExp);
break;
case "not_between":
expression = NotBetween(type, rule.Value, propertyExp);
break;
case "less":
expression = LessThan(type, rule.Value, propertyExp);
break;
case "less_or_equal":
expression = LessThanOrEqual(type, rule.Value, propertyExp);
break;
case "greater":
expression = GreaterThan(type, rule.Value, propertyExp);
break;
case "greater_or_equal":
expression = GreaterThanOrEqual(type, rule.Value, propertyExp);
break;
case "begins_with":
expression = BeginsWith(type, rule.Value, propertyExp);
break;
case "not_begins_with":
expression = NotBeginsWith(type, rule.Value, propertyExp);
break;
case "contains":
expression = Contains(type, rule.Value, propertyExp);
break;
case "not_contains":
expression = NotContains(type, rule.Value, propertyExp);
break;
case "ends_with":
expression = EndsWith(type, rule.Value, propertyExp);
break;
case "not_ends_with":
expression = NotEndsWith(type, rule.Value, propertyExp);
break;
case "is_empty":
expression = IsEmpty(propertyExp);
break;
case "is_not_empty":
expression = IsNotEmpty(propertyExp);
break;
case "is_null":
expression = IsNull(propertyExp);
break;
case "is_not_null":
expression = IsNotNull(propertyExp);
break;
default:
throw new Exception($"Unknown expression operator: {rule.Operator}");
}

}
return expression;


}
return null;

}

最佳答案

if it were a static LINQ, it should have been like

u => u.Informations.FirstOrDefault(i => i.id == 2)?.Value == "something"))

Operator ?. 在表达式树中不受支持,因此最好构建如下内容:

u => u.Informations.Any(i => i.id == 2 && i.Value == "something"))

为此,在单独的方法中提取原始表达式构建:

private static Expression BuildCondition(FilterRule rule, Expression propertyExp)
{
Type type;

switch (rule.Type)
{
case "integer":
type = typeof(int);
break;
case "long":
type = typeof(long);
break;
case "double":
type = typeof(double);
break;
case "string":
type = typeof(string);
break;
case "date":
case "datetime":
type = typeof(DateTime);
break;
case "boolean":
type = typeof(bool);
break;
default:
throw new Exception($"Unexpected data type {rule.Type}");
}

Expression expression;

switch (rule.Operator.ToLower())
{
case "in":
expression = In(type, rule.Value, propertyExp);
break;
case "not_in":
expression = NotIn(type, rule.Value, propertyExp);
break;
case "equal":
expression = Equals(type, rule.Value, propertyExp);
break;
case "not_equal":
expression = NotEquals(type, rule.Value, propertyExp);
break;
case "between":
expression = Between(type, rule.Value, propertyExp);
break;
case "not_between":
expression = NotBetween(type, rule.Value, propertyExp);
break;
case "less":
expression = LessThan(type, rule.Value, propertyExp);
break;
case "less_or_equal":
expression = LessThanOrEqual(type, rule.Value, propertyExp);
break;
case "greater":
expression = GreaterThan(type, rule.Value, propertyExp);
break;
case "greater_or_equal":
expression = GreaterThanOrEqual(type, rule.Value, propertyExp);
break;
case "begins_with":
expression = BeginsWith(type, rule.Value, propertyExp);
break;
case "not_begins_with":
expression = NotBeginsWith(type, rule.Value, propertyExp);
break;
case "contains":
expression = Contains(type, rule.Value, propertyExp);
break;
case "not_contains":
expression = NotContains(type, rule.Value, propertyExp);
break;
case "ends_with":
expression = EndsWith(type, rule.Value, propertyExp);
break;
case "not_ends_with":
expression = NotEndsWith(type, rule.Value, propertyExp);
break;
case "is_empty":
expression = IsEmpty(propertyExp);
break;
case "is_not_empty":
expression = IsNotEmpty(propertyExp);
break;
case "is_null":
expression = IsNull(propertyExp);
break;
case "is_not_null":
expression = IsNotNull(propertyExp);
break;
default:
throw new Exception($"Unknown expression operator: {rule.Operator}");
}

return expression;
}

在直接成员的当前分支中使用它,并在“索引”分支中使用以下内容:

if (rule.Field.StartsWith("ix_"))
{
var tokens = rule.Field.Split("_");
var infoParameter = Expression.Parameter(typeof(UserInformation), "i");
var infoCondition = Expression.AndAlso(
Expression.Equal(
Expression.Property(infoParameter, nameof(UserInformation.Id)),
Expression.Constant(long.Parse(tokens[1]))),
BuildCondition(rule, Expression.Property(infoParameter, tokens[2])));
return Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new[] { infoParameter.Type },
Expression.Property(pe, nameof(User.Informations)),
Expression.Lambda(infoCondition, infoParameter));
}

作为旁注,C# 逻辑 &&|| 运算符由 Expression.AndAlsoExpression.OrElse 表示

关于c# - 列表上的动态查询选择包含列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55894756/

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