我正在尝试确定MemberExpression
是否属于某个对象实例,或者它是否只是表达式中的参数。我提出了我认为可行的解决方案,但我想首先通过SE运行。把这两个表达式作为首发者:
var myClassInstance = new MyClass();
Expression<Func<MyClass, bool>> exp1 = mc => myClassInstance.MyBool;
Expression<Func<MyClass, bool>> exp2 = mc => mc.MyBool;
exp1
包含对局部变量/字段的引用。在我的代码中,.MyBool
将被编译为检索其值,如下所示:
Expression.Lambda<Func<object>>(Expression.Convert(exp1.Body as MemberExpression, typeof(object))).Compile();
在exp2
中,.MyBool
不是来自对象引用,而是来自参数。如果我尝试编译.MyBool
表达式,则会抛出异常variable 'mc' of type 'ConsoleLol.MyClass' referenced from scope '', but it is not defined
。
经过一番努力,我想出了这个潜在的解决方案:
var exp1parent = (exp1.Body as MemberExpression).Expression;
var exp2parent = (exp2.Body as MemberExpression).Expression;
Console.WriteLine(exp1parent.NodeType);
Console.WriteLine(exp2parent.NodeType);
看来,如果表达式的父表达式不是实例,NodeType
属性将是Parameter
。否则,在这种情况下,它将是MemberAccess
。
我在这个结论中是否正确,或者是否有更理想的方法来执行此检查?
答案 0 :(得分:0)
如果我理解正确,您就构建了一个SQL提供程序,因此您需要评估所有无法转换为SQL的内容。有关如何执行此操作的说明,请参阅Matt Warren的文章Building an IQueryable Provider – Part III,或者您可以将其用作iqtoolkit库中的PartialEvaluator
。
答案 1 :(得分:0)
在这里回答我自己的问题。
这可能是一项正在进行中的工作,但到目前为止它仍然有效。在遍历表达式树并尝试确定是否应将属性表达式解释为数据库字段或是否应为SQL值参数进行编译时,您需要知道表达式是否来自lambda表达式参数。如果没有,并且您尝试编译/调用它,您将收到我的OP中提到的错误。
这是我对初学者的看法。您基本上只是继续跳过表达式树,直到父表达式NodeType == ExpressionType.Parameter
或父表达式为空。如果是前者,它是一个参数后代,所以不要编译它。如果是后者,它代表一个数据库字段,所以要相应地解析它。
public static bool IsParameterDescendent(this Expression Expression)
{
var pe = Expression;
while (pe != null && pe.NodeType != ExpressionType.Constant)
{
if (pe.NodeType == ExpressionType.MemberAccess)
{
pe = pe.TryCast<MemberExpression>().Expression;
}
else if (pe.NodeType == ExpressionType.Call)
{
pe = pe.TryCast<MethodCallExpression>().Object;
}
if (pe?.NodeType == ExpressionType.Parameter) return true;
}
return false;
}
编辑:您可以避免必须完全执行此操作的一种方法是始终将数据库属性放在运算符的左侧,将invokable / value提供程序放在右侧。不过,我不喜欢被限制,因此这种方法。