解析传递给LINQ表达式的方法的LINQ参数

时间:2011-09-23 15:53:26

标签: c# linq expression expression-trees

我正在研究LINQ to SQL转换器。它应该将LINQ查询转换为SQL。我专注于创建查询的WHERE部分。 我正在遍历LINQ表达式树,然后我遇到一个问题,我无法获得传递给包含LINQ调用的方法的实际参数的值。

注意:我没有使用LINQ to SQL,也没有使用Entity Framework,也没有使用NHibernate,因为我无法在VB6 Legacy系统中使用它。

所以我想要达到的目的是在高级别的地方打电话:

int? parameterForCall = cmb.SelectedValue;

然后我有一个像这样的方法,它在ExpenseDAL类中调用BaseDAL<Expense>.GetAll(X)

public IList<Expense> GetAll(int? parameterForCall)
{
    IList<Expense> expenses = BaseDAL<Expense>.GetAll(t => t.Fixed == 
                                                           parameterForCall);
}

GetAll方法有这样的签名:

public static IList<T> GetAll(Expression<Func<T, bool>> predicate = null);

然后我调用将表达式转换为SQL的GetCondition方法:

private static string GetCondition(Expression predicate = null);

这是一个递归函数。在其中我遇到了一个情况,我需要获取传递给GetAll表达式的参数,名为parameterForCall

问题是我可以这样写:

dynamic value = (predicate as ConstantExpression);

在ImmediateWindow中我可以看到value.Value是这样写的:

{FMC.Proxy.Common.BaseDAL.}
    parameterForCall: 19

但我无法达到值19.我想要它,所以我可以将它转换为值以放入SQL字符串。

有没有人知道如何获得价值19所以我可以在代码中使用它?

3 个答案:

答案 0 :(得分:1)

您只需从已经获得的dynamic获取该属性:

dynamic value = (predicate as ConstantExpression);
int? parameterForCall = value.parameterForCall;

如果您不知道参数的名称(可能还有类型),则可以使用反射。您要查找的参数作为ConstantExpression.Value返回的对象的公共字段存在。这不是任何地方指定的,因此使用它需要您自担风险。

这段小代码证明:

class Expense { public int Fixed { get; set; } }

void Test(int? parameterForCall) {
  Expression<Func<Expense, bool>> predicate = t => t.Fixed == parameterForCall;
  var parameter = (
    (ConstantExpression) (
      (MemberExpression) (
        (BinaryExpression) predicate.Body
      ).Right
    ).Expression
  ).Value;
  var fieldInfo = parameter.GetType().GetFields().First();
  var name = fieldInfo.Name;
  var value = fieldInfo.GetValue(parameter);
  Console.WriteLine(name + " = " + value);
}

如果您执行Test(19),则输出为parameterForCall = 19

答案 1 :(得分:1)

我的回答是假设 - 并且您的问题支持此假设 - 传递到GetCondition的谓词属于ConstantExpression类型。

获取该值并非易事,因为parameterForCall是在自动生成的类中捕获的。当您查看(predicate as ConstantExpression).Value.GetType()的输出时,您可以看到。在我的例子中,这输出:

UserQuery+<>c__DisplayClass0

这个类又有一个名为parameterForCall的公共字段。现在,您有两种可能性来获得该值:

  1. 您知道该字段的名称,因为您可以控制创建此表达式的方法。在这种情况下,您可以使用此代码获取值:

    var constantExpression = (ConstantExpression)predicate;
    dynamic autoGeneratedClass = constantExpression.Value;
    object value = autoGeneratedClass.parameterForCall;
    

    value.GetType()将返回System.Int32value将是一个带有值19的盒装int。

  2. 您不知道该字段的名称。在那种情况下,它更难。您可以使用反射来获取唯一的公共可见字段的值,但是如果这样做,您会对自动生成的类做出很多假设。

答案 2 :(得分:0)

希望这个Stackoverflow回答有帮助

expression trees linq get value of a parameter?

基本上,您可能必须先编译表达式,然后才能以编程方式获取属性值。

但要注意性能损失。祝你好运。