Ale*_*ler 2 c# asp.net lambda expression
我收到了客户发送来的条件清单。我需要获取此列表并创建一个由 EntityFramework 执行的动态 where 子句。
每个条件都有一个运算符、一个属性和一个右侧值。
每个条件列表都需要进行“与”运算。
每个条件列表列表都需要进行“或”运算。
所以如果我们有
{
"ConditionLists":[
[
{
"LhsAttributeDefinition":{
"attribute":{
"key":"isHighBandwidth",
"value":"IsHighBandwidth"
}
},
"Operator":{
"name":"equals",
"description":"=",
"validation":"",
"inputType":"dropdown"
},
"RhsValue":"true"
},
{
"LhsAttributeDefinition":{
"attribute":{
"key":"isForMobile",
"value":"IsForMobile"
}
},
"Operator":{
"name":"equals",
"description":"=",
"validation":"",
"inputType":"dropdown"
},
"RhsValue":"true"
}
],
[
{
"LhsAttributeDefinition":{
"attribute":{
"key":"isHighBandwidth",
"value":"IsHighBandwidth"
}
},
"Operator":{
"name":"equals",
"description":"=",
"validation":"",
"inputType":"dropdown"
},
"RhsValue":"true"
},
{
"LhsAttributeDefinition":{
"attribute":{
"key":"isForTablet",
"value":"IsForTablet"
}
},
"Operator":{
"name":"equals",
"description":"=",
"validation":"",
"inputType":"dropdown"
},
"RhsValue":"true"
}
]
]
}
Run Code Online (Sandbox Code Playgroud)
那应该生成.Where(x => (x.isHighBandwidth == true && x.isForMobile == true) || (x.isHighBandwidth == true && x.isForTablet == true))
以下是我使用表达式库完成此任务所需的内容:
MethodInfo contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
Expression finalExpression = null;
List<ParameterExpression> paramsArray = new List<ParameterExpression>();
foreach (var conditionList in conditionLists)
{
Expression andGroup = null;
foreach (var condition in conditionList)
{
Expression expression = null;
ParameterExpression param = null;
ConstantExpression constant = null;
switch (condition.LhsAttributeDefinition.Attribute.Key)
{
case "title":
param = Expression.Parameter(typeof(string), "LearningItem.Title");
constant = Expression.Constant(condition.RhsValue, typeof(string));
expression = Expression.Call(param, contains, constant);
break;
case "isHighBandwidth":
param = Expression.Parameter(typeof(string), "IsHighBandwidth");
constant = Expression.Constant(condition.RhsValue, typeof(string));
expression = Expression.Equal(param, constant);
break;
case "isForMobile":
param = Expression.Parameter(typeof(string), "IsForMobile");
constant = Expression.Constant(condition.RhsValue, typeof(string));
expression = Expression.Equal(param, constant);
break;
case "isForTablet":
param = Expression.Parameter(typeof(string), "IsForTablet");
constant = Expression.Constant(condition.RhsValue, typeof(string));
expression = Expression.Equal(param, constant);
break;
}
paramsArray.Add(param);
if (andGroup != null)
{
Expression.And(andGroup, expression);
}
else
{
andGroup = expression;
}
}
//OR the expression tree created above
if (finalExpression != null)
{
Expression.Or(finalExpression, andGroup);
}
else
{
finalExpression = andGroup;
}
}
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { query.ElementType },
query.Expression,
Expression.Lambda<Func<Activity, bool>>(finalExpression, paramsArray.ToArray<ParameterExpression>()));
return query;
Run Code Online (Sandbox Code Playgroud)
所以我的想法是,在嵌套的 for 循环内部,我将 AND 查询和 OR 查询构建为一个大表达式,然后在最后创建 lambda 查询。我一路收集参数到 paramsArray(列表)中。
我的问题是,在执行时,它会爆炸说'ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'INOLMS.Data.Activity''
。我假设这是因为我到目前为止收集的参数只是一个字符串(我的示例请求正文只是 IsHighBandwidth true 的单个条件),并且它不喜欢我采用字符串参数并尝试获取Activity
查询。
我在这里做错了什么?
您当前的代码存在很多问题。让我们从具体情况开始。
假设你想转型
{
"LhsAttributeDefinition":{
"attribute":{
"key":"isHighBandwidth",
"value":"IsHighBandwidth"
}
},
"Operator":{
"name":"equals",
"description":"=",
"validation":"",
"inputType":"dropdown"
},
"RhsValue":"true"
}
Run Code Online (Sandbox Code Playgroud)
进入.Where(x => x.IsHighBandwidth == true)
。
因此,首先您必须构建表达式的左侧,并且x.IsHighBandwidth
您不能简单地定义具有常量值的字符串类型的参数IsHighBandwidth
(这就是您在 中所做的Expression.Parameter(typeof(string), "IsHighBandwidth")
。要执行此操作,您需要第一个类型的参数Activity
,然后使用Expression.MakeMemberAccess
适当的参数进行调用MemberInfo
代表所需属性的对象。像这样:
var p = Expression.Parameter(typeof(Activity));
var accessorExp = Expression.MakeMemberAccess(p, typeof(Activity).GetProperty("IsHighBandwidth"));
Run Code Online (Sandbox Code Playgroud)
现在我们已经处理了左侧,让我们看看右侧。如果您的属性是类型bool
并且您想要进行相等检查,那么右侧也必须匹配。您不能简单地创建字符串常量并期望某种魔法将其解析为bool
类型。在我们的例子中,我们知道我们期望 bool 值,因此我们必须首先将字符串解析为 bool,然后创建类型的常量表达式bool
:
bool value = Boolean.Parse(condition.RhsValue); // add error checks
var valueExpr = Expression.Constant(value);
Run Code Online (Sandbox Code Playgroud)
现在我们已经处理了左侧和右侧并且类型正确,您可以像在代码中那样构造相等表达式:
var expression = Expression.Equal(accessorExpr, valueExpr);
Run Code Online (Sandbox Code Playgroud)
现在我们已经构建了主体(使用bool
表达式类型),我们必须构造 lambda,它将作为参数传递给 lambda。正如您在 C# 代码中看到的,此 lambda 只接受一个类型参数Activity
并返回bool
。您无法像在代码中那样发送多个参数。例子:
// Parameter p must be the same as was defined above
var lambda = Expression.Lambda(expression, new [] { p });
Run Code Online (Sandbox Code Playgroud)
现在我们有了主体,您可以Where
像在代码中那样构造新的方法调用表达式,但有一个重要的区别:如果您希望外部参数起作用,则必须引用 lambda 表达式(这就是 LINQWhere
方法在幕后所做的事情):
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { query.ElementType },
query.Expression,
Expression.Quote(lambda));
Run Code Online (Sandbox Code Playgroud)
这应该足够详细,可以帮助您入门。您必须记住,LINQ 表达式确实是低级的,您必须注意自己生成有效的表达式树。在 C# 中编程时,不存在您可能会用到的编译器魔法(例如隐式转换)。
归档时间: |
|
查看次数: |
1373 次 |
最近记录: |