4

I have built my own SQL Query builder that breaks apart an Expression, however, I'm having an issue trying to get the value of string defined in the same function as the lambda expression.

Here is what I am trying to do in console app:

private static void MyBuilderTest()
{
    var sqlBuilder = new SqlBuilder();

    // Doesn't work -- NEED GUIDANCE HERE
    var testValue = "Test";  // Defined in the same function as the lambda below

    sqlBuilder.Select<FooObject>(o => o.FooValue == testValue);

    // Works
    var someObject = new SomeObject { SomeValue = "classTest };

    sqlBuilder.Select<FooObject>(o => o.FooValue == someObject.SomeValue);
}

In my builder it subclasses from ExpressionVisitor, and I override the VisitMember. I found that a string defined in at the base Console level will come back as:

Node.Expression.NodeType == ExpressionType.Constant

The Node.Expression passes back properties of:

CanReduce = false
DebugView = ".Constant<ConsoleApplication1.Program+<>c__DisplayClass1>(ConsoleApplication1.Program+<>c__DisplayClass1)"
NodeType = Constant
Type = System.Type {System.RunetimeType}
Value = {ConsoleApplication1.Program}

The Node.Expression.Value contains:

testValue = "Test" (Type: string)

How do I get this value? I've tried several things, like:

var memberType = node.Expression.Type.DeclaringType;

This passes back a ConsoleApplication1.Program type.

However, when I do:

 memberType.GetProperty("testValue");   // Declaring Type from Expression

It passes back null.

The above methods work fine if I place the lambda "strings" in a class, but doesn't work if they string is defined in the console function.

Can anyone tell me how to get the string value if it's defined at the function level of the lambda?

EDITED: Added VisitMember

protected override Expression VisitMember(MemberExpression node)
{
    if (node.NodeType == ExpressionType.Constant)
    {
        // Node.Expression is a ConstantExpression type.
        // node.Expression contains properties above
        // And Has Value of:  {ConsoleApplication1.Program}
        // Expanding Value in Watch window shows:  testValue = "Test"

        // How do I get this value, if the ConsoleApplication1.Program type doesn't
        // even know about it?  Looks like maybe a dynamic property?
    }
 }

EDITED

Added code to the console app example to show what works and what doesn't.

4
  • 1
    I think you're going about this wrong. You should be trying to find the ConstantExpression and getting the value from that. I would advise against using reflection as you're doing, as that's kind of re-inventing the wheel (and of course is no help at all if you're interrorgating expressions rather than fields/properties). However, to help more, can you please provide more context around your builder code -- where's it's getting node, etc? Commented Jan 11, 2013 at 0:57
  • "node" is passed in the override of VisitMember and is a MemberExpression type. Commented Jan 11, 2013 at 0:58
  • so interrogate that node more -- it's a tree, and one of the child nodes will be an instance of ConstantExpression, which has a Value property of type object that will contain the literal testValue. (actually, you're closing over a variable, so it's probably a compiler generated type; possibly you even need GetField instead -- I foget if the compiler generates classes with fields or properties) Commented Jan 11, 2013 at 0:59
  • That's the point. I can see Value with testValue = test (in the Watch window during debugging), but how do I get it? How do I get this value if the object is a "object" type and doesn't know about the "testValue" property. The Node.Expression.Value is at the bottom of the tree, so anything I do always passes back {ConsoleApplication3.Program}. Commented Jan 11, 2013 at 1:01

2 Answers 2

2

The lambda in your example has "closed over" the testValue variable, meaning the compiler has captured it as a field of the same name in an automatically generated class called ConsoleApplication1.Program+<>c__DisplayClass1>. You can use normal reflection to get the current value of that field by casting the right hand-side of the binary expression into a MemberExpression.

var testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var rhs = (MemberExpression) ((BinaryExpression) expr.Body).Right;
var obj = ((ConstantExpression) rhs.Expression).Value;
var field = (FieldInfo) rhs.Member;
var value = field.GetValue(obj);
Debug.Assert(Equals(value, "hello"));
testValue = "changed";
value = field.GetValue(obj);
Debug.Assert(Equals(value, "changed"));

Alternatively you can change your variable into a constant.

const string testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var value = ((ConstantExpression) ((BinaryExpression) expr.Body).Right).Value;
Debug.Assert(Equals(value, "hello"));
Sign up to request clarification or add additional context in comments.

Comments

0

Instead of doing this by yourself, have a look at PartialEvaluator from Matt Warren. It replaces all references to constants with the constants themselves.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.