2

I am working on LINQ to SQL translator. It should translate LINQ queries to SQL. I am focused on creating WHERE part of the query. I am traversing LINQ Expression tree and then I get to a problem that I can't get to the value of actual parameter passed to the method that contains LINQ call.

NOTE: I am not using LINQ to SQL, nor Entity Framework, nor NHibernate because I simply can't since VB6 Legacy system is in the game.

So what I am trying to achieve is to call somewhere on high level this:

int? parameterForCall = cmb.SelectedValue;

Then I have a method like this and it is in ExpenseDAL class that calls BaseDAL<Expense>.GetAll(X):

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

GetAll method has signature like this:

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

Then I am calling GetCondition method that converts expression to SQL:

private static string GetCondition(Expression predicate = null);

It is a recursive function. In it I get to a situation that I need to get to parameter that I passed to GetAll expression, named parameterForCall.

The problem is that I can write this:

dynamic value = (predicate as ConstantExpression);

And in ImmediateWindow I can see that value.Value is written like this:

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

But I can't get to the value 19. And I want it so I can convert it to value to put to SQL string.

Does anyone know how to get to value 19 so I can use it in the code?

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
gljivar
  • 440
  • 7
  • 19

3 Answers3

1

You simply have to get the property from the dynamic you already got:

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

If you don't know the name of the parameter (and perhaps the type) you can use reflection. The parameter you are looking for exists as a public field of the object returned by ConstantExpression.Value. This is not something specified anywhere so use it at your own risk.

This small piece of code demonstrates that:

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);
}

If you execute Test(19) the output is parameterForCall = 19.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
1

My answer assumes - and your question supports this assumption - that the predicate passed into GetCondition is of type ConstantExpression.

It isn't trivial to get that value, because parameterForCall is captured in an automatically generated class. You can see that, when you look at the output of (predicate as ConstantExpression).Value.GetType(). In my case, this outputs:

UserQuery+<>c__DisplayClass0

This class in turn has a public field named parameterForCall. Now, you have two possibilities to get that value:

  1. You know the name of that field, because you control the method in which this Expression is created. In that case, you can use this code to get the value:

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

    value.GetType() will return System.Int32 and the value will be a boxed int with the value 19.

  2. You don't know the name of that field. In that case, it is harder. You could use reflection to get the value of the only public visible field, but if you do this, you would make a lot of assumptions about the automatically generated class.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • I would like to know how you can reflect on `autoGeneratedClass`. Reflecting on dynamic objects is different compared to statically typed objects. – Martin Liversage Sep 23 '11 at 16:58
  • You don't reflect on `autoGeneratedClass`. You reflect of `constantExpression.Value`, which is an `object`. If you go with option 2 "reflection", you wouldn't need `autoGeneratedClass`... – Daniel Hilgarth Sep 23 '11 at 17:07
  • @Martin: Haha, did you update your answer with the help of my comment here? Wow, that's just cheap. Especially because you left out the warning I posted about this method! – Daniel Hilgarth Sep 24 '11 at 08:09
  • @Martin: My last comment didn't hit the right tone and calling you cheap was wrong. I am sorry about that. But: I intentionally didn't provide code for this scenario, because I wanted the OP to ask for it, if he really wants to go this way. Why? Because more research on my side would have been needed to better name the possible problems with this approach. Basically: (1) "Closure class reuse" by the compiler in such a way, that multiple variables are captured in the closure class and thus, simply taking the first public field could give the wrong result. (2) Relying on implementation details. – Daniel Hilgarth Sep 25 '11 at 08:15
  • Thanky, I had to modify some parts but I got on the right track. Also, is there a way to determine if value.Type is dynamically generated? Now I am looking if it's name starts with "<>". var value = (predicate as ConstantExpression); dynamic evaluatedValue = value.Value; if (value.Type.Name.StartsWith("<>")) { evaluatedValue = value.Type.GetFields()[0].GetValue(value.Value); } condition += GetSQLFormattedColumnValue((object)evaluatedValue); – gljivar Oct 24 '11 at 07:38
  • @gljivar: I don't know of any... Why do you need such functionality? – Daniel Hilgarth Oct 24 '11 at 08:17
0

Hope this Stackoverflow answer helps

expression trees linq get value of a parameter?

Basically you might have to compile the expression before being able to programmatically get the property value.

Watch for performance penalty though. Good luck.

Community
  • 1
  • 1
Anas Karkoukli
  • 1,342
  • 8
  • 13