使用表达式树创建完全动态的 where 子句并在 IQueryable 上执行
在代码中的第 (3) 点,我定义了一个名为 query1 的查询,其中定义了 .Where lambda 表达式。该查询在某种程度上是动态的,但仍然包含静态元素,它始终引用类型 Employee 及其 (int) 属性 ClientID。
现在我非常喜欢基于方法参数来动态引用类型及其属性,方法参数如下面第 (1) 点所示。
到目前为止,我尝试的是将第 (3) 点下定义的查询的静态部分完全动态化,方法是用 (4)、(5) 和 (5) 中所写的更复杂的表达式树替换它。 (6)。但是当我尝试将所有内容添加在一起时,它说我使用错误的参数调用 .Where 。我不知道如何使用正确的参数调用 .Where 来创建完全动态的选择。
有人知道解决这个问题吗?我花了一天的时间寻找,目前还没有找到解决方案。
dsMain domainService = new dsMain();
//(1)i want to rewrite the following four variables to method-parameters
Type entityType = typeof(Employee);
String targetProperty = "ClientID";
Type entityProperty = typeof(Employee).GetProperty(targetProperty).PropertyType;
int idToDelete = 5;
//(2)create expression-function: idToDelete == entityType.targetProperty (in this case: Employee.ClientID)
ParameterExpression numParam = Expression.Parameter(entityProperty, targetProperty.Substring(0, 3));
ConstantExpression equalTarget = Expression.Constant(idToDelete, idToDelete.GetType());
BinaryExpression intEqualsID = Expression.Equal(numParam, equalTarget);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
intEqualsID,
new ParameterExpression[] { numParam });
//(3)I want to create query1 fully dynamic, so defining Employee or an other type and its property at run time
WhereClause = lambda1.Compile();
IQueryable<Employee> employees = domainService.GetEmployees();
var query1 = employees.Where<Employee>(C => WhereClause.Invoke(C.ClientID)).Expression;
//(4)create the operand body {value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
var operandbodyMethod = WhereClause.GetType().GetMethod("Invoke");
var operandbodyType = typeof(System.Boolean);
var operandbodyArgs1Expression = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
var operandbodyArgs1 = Expression.MakeMemberAccess(operandbodyArgs1Expression, entityType.GetMember(targetProperty)[0]);
var operandBodyObjectExp = Expression.Constant(this, this.GetType());
var operandbodyObject = Expression.MakeMemberAccess(operandBodyObjectExp, this.GetType().GetMember("WhereClause")[0]);
//(5)create the operand {E => value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
var operandbody = Expression.Call(operandbodyObject, operandbodyMethod, operandbodyArgs1);
var operandParameter = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
var operandType = typeof(Func<,>).MakeGenericType(entityType, typeof(System.Boolean));
//(6)
var operand = Expression.Lambda(operandType, operandbody, new ParameterExpression[] { operandParameter });
var expressionType = typeof(Expression<>).MakeGenericType(operandType);
var completeWhereExpression = Expression.MakeUnary(ExpressionType.Quote, operand, expressionType);
//(7)the line below does not work
var query2 = employees.Where<Employee>(completeWhereExpression).Expression;
非常感谢您阅读我的问题! 如果您对我的问题有疑问,请询问他们:)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这很难单独看待,但首先发生的事情是
Compile
看起来与IQueryable
不合适 - 这很少工作(LINQ 到对象是例外)。与
WhereClause.Invoke(C.ClientID)
等效的是使用Expression.Invoke
调用子表达式,但即使这样也很不稳定:LINQ-to-SQL 将支持它,EF(至少在 3.5 中)不支持(也许“不”;我没有在 4.0 中重新检查过)。最终,如果可能的话,将lambda1
创建为Expression>
会更加稳健:然后将 this 传递给地点:
This is quite hard to look at in isolation, but the first thing that occurs is that
Compile
looks out of place forIQueryable
- that will rarely work (LINQ-to-Objects being the exception).An equivalent to
WhereClause.Invoke(C.ClientID)
is to useExpression.Invoke
to call a sub-expression, but even this is flakey: LINQ-to-SQL will support it, EF (in 3.5 at least) doesn't (maybe "didn't"; I haven't re-checked in 4.0). Ultimately, it would be more robust to createlambda1
as anExpression<Func<Employee,bool>>
if possible:Then pass this to
Where
: