C# MemberExpression for a specific dictionary item - c#

I am trying to dynamically generate binary expressions using Expression.MakeBinary:
public static BinaryExpression MakeBinary(
ExpressionType binaryType,
Expression left,
Expression right
)
A simple example of that for Property "Bar" of class "Foo" would be:
var param = Expression.Parameter(typeof(Foo));
var left = MemberExpression.Property(param, "Bar");
var propertyType = typeof(Foo).GetProperty("Bar").PropertyType;
var right = Expression.Constant(Convert.ChangeType("newValue", propertyType ));
var expression = Expression.MakeBinary(ExpressionType.Equal, left, right);
My goal is to be able to do the same thing as above, but instead of comparing "Bar" property with "newValue", in my case "Bar" is a dictionary and I want to compare a specific entry of that dictionary with "newValue". What I want to achieve would look like this (but this code is not valid):
var param = Expression.Parameter(typeof(Foo));
var left = MemberExpression.Property(param, "Bar[\"entryName\"]");
I would suppose what I am trying to do is possible, but I can't seem to find the right syntax/class/method to use. Obviously the code above returns an error, since Bar["entryName"] is not a property. But how can I build the expression based on a specific entry in a dictionary for a given Type?
The high level application of this is to be able to apply rules, using field/operator/compareValue as parameters, but being able to indicate the "path" to that field (depending on the class, sometimes it would be a plain property, or as asked here, an entry in a dictionary, etc.).
Thank you

You can use Expression.MakeIndex():
var param = Expression.Parameter(typeof(Foo));
var bar = MemberExpression.Property(param, "Bar");
Type dictionaryType = typeof(Foo).GetProperty("Bar").PropertyType;
PropertyInfo indexerProp = dictionaryType.GetProperty("Item");
var dictKeyConstant = Expression.Constant("entryName");
var dictAccess = Expression.MakeIndex(bar, indexerProp, new[] {dictKeyConstant});
var propertyType = indexerProp.PropertyType;
var right = Expression.Constant(Convert.ChangeType("newValue", propertyType ));
var expression = Expression.MakeBinary(ExpressionType.Equal, dictAccess, right);
The property name of an indexer is always "Item". The dictKeyConstant should be the expression for your dictionary's key.

Related

Combine property selector expression tree and value to create a predicate for EF filtering - create filter from lambda selector and value

Given a simple class with arbitrary properties (for discussion lets say Id, Name, and Description)
and given an instance of that class, I want to find matching entries in the database by specifying the property to match
I'm trying to do something in this respect similar to the AddOrUpdate method of EF, but I need the entity returned to me for further processing.
var match = new SomeClass{Name="Whatever"};
var found = Context.SomeClass.Find(x=>x.Name, match);
public static T Find<T>(this DbSet<T> set, Expression<Func<T, object>> matchOn, T matchAgainst) where T : class {
var func = matchOn.Compile();
var valueToFind = func(matchAgainst);
var combinedExpression = //matchon + "=" + valueToFind;
var found = set.FirstOrDefault(combinedExpression);
return found;
}
That gives me the value of the property in the passed in object, but I need to now combine that value with the passed in expression and pass it to the db set.
IE, the code I'm effectively trying to run is set.FirstOrDefault(x=>x.Name==valueToFind) How do I take the matchon expression (which contains x=>x.Name) and combine that with the ==valueToFind to get the x=>x.Name==valueToFind from them?
How do I build the combined expression? (I realize the "string" code above is completely wrong, but I was trying to get across what I need the function to do, but I have no idea what that syntax would look like.)
For manually coded examples, it would be easy enough just to pass in a hardcoded lambda with the value set, but my use case involves running through a collection of objects and finding the match for each one, so the value will not be known until runtime, and the method must work against arbitrary types and various properties, so I can't hardcode the property name either.
If you have a property selector, and a value to compare to, you can get an expression tree like this:
public static Func<TEntity, bool> GetComparer<TEntity,TProperty>(
Expression<Func<TEntity,TProperty>> selector, TProperty value)
{
var propertyRef = selector.Body;
var parameter = selector.Parameters[0];
var constantRef = Expression.Constant(value);
var comparer
= Expression.Lambda<Func<TEntity, bool>>
(Expression.Equal(propertyRef, constantRef), parameter)
.Compile();
return comparer;
}
Sample usage:
var comparer = GetComparer<Person, string>(p => p.Name, "John");
var persons = Person.GetPersons();
var john = persons.FirstOrDefault(comparer);

Return value of property based on property name

How do I build expression tree in C# that returns value of a property based on the name of the property
Func<Foo, long> getValue(string propertyName)
{
// i think that the beginning of the expression tree would look like this
// but i'm not sure this is correct
var inputParameter = Expression.Parameter(typeof(Foo));
var desiredProperty = typeof(Foo).GetProperty(propertyName);
var valueOfProperty = Expression.Property(inputParameter, desiredProperty);
// ... ??? todo: expression that returns value
}
Call to this function looks like this which is part of another expression that is passed to Linq's Select method:
value = getValue("Bar").Invoke(FooInstance)
Should be enough:
var lambda = Expression.Lambda<Func<Foo, long>>(valueOfProperty, inputParameter);
return lambda.Compile();
Anyway - what's the purpose for building Expression when you could get value directly via reflection?
return someFoo => (long)desiredProperty.GetValue(someFoo);

Create Generic Expression from string property name

I have a variable called sortColumn, which contains the text of a column that I want to sort a query result by. I also have a generic repository which takes as a parameter an Expression that contains the field I want to sort by. I can't seem to get from the string property name to an Expression.
So the generic repository that I have contains the following method
public IEnumerable<TEntity> Get<TOrderBy>(Expression<Func<TEntity, bool>> criteria,
Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
int pageSize,
bool isAssendingOrder = true,
EnumDeletePolicy deletePolicy = EnumDeletePolicy.ExcludeDeleted)
Notice the second parameter to this Get is Expression-Func-TEntity, TOrderBy. As I mentioned I have a variable called sortColumn, which contains the string for a property on my TEntity object I need to convert this string into an Expression that I can pass to the Get method.
Here is what I have right now.
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
Which creates an object of type LambdaExpression. The actual type of this LambdaExpression is an Expression-Func-IContract, string (or whatever the type sortColumn of the property is). If I call the Get method and pass in this LambdaExpression and explicitly cast it to the Expression type then it will work fine. The problem is I don't know what the Expression type is, it could be a string, int, int?, etc. It all depends on the type of the property that is specific in the sortColumn property.
Can you help me make this last jump to the right Expression type?
Edit based on Marc's suggestions:
I nearly have this working, actually based specifically on the question it is working, but I have 1 remaining problem.
The IContract which is the Entity Type that I'm querying against actually inherits from IRelationship. If I specify a field from the IContract interface then the code above works. If I specify a field from the IRelationship interface then the following line fails.
var memberExpression = Expression.Property(parameter, data.SortColumn);
If I try something like below so that I'm grabbing the MemberExpression from the IRelationship, but building the Lambda based on IContract I get an error from the repository.
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(parameter, data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, Expression.Parameter(typeof(IContract)));
The error that I get is "The parameter '' was not bound in the specified LINQ to Entities query expression."
The final expression to get it working was this
var parameter = Expression.Parameter(typeof(IContract));
var memberExpression = Expression.Property(parameter, typeof(IRelationship), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);
So I needed to specify the middle parameter to the memberExpression line, to say look in the inherited Relationship interface for the property
You kinda need to use the correct generic overload - which used to mean you had to use MakeGenericMethod; however, you can also use dynamic to avoid the need to use MakeGenericMethod here, for example (in this case via Where, but the important point is how it works):
IQueryable<Foo> source = new[] { new Foo { Bar = 123 } }.AsQueryable();
Expression<Func<Foo,bool>> typed = x=>x.Bar == 123;
LambdaExpression untyped = typed;
IQueryable<Foo> filtered = Queryable.Where(source, (dynamic)untyped);
Note: you can't use extension methods here - hence why you need to use Queryable.*.
For an OrderBy example using your code:
var parameter = Expression.Parameter(typeof(Foo));
var memberExpression = Expression.Property(parameter, "Bar");
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
LambdaExpression untyped = lambdaExpression;
IQueryable<Foo> sorted = Queryable.OrderBy(source, (dynamic)untyped);
var all = sorted.ToArray();
Re the edit:
var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(
Expression.Convert(parameter, typeof(IContract)), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);

Expression Tree with string assignment and getting value

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.
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"));
Instead of doing this by yourself, have a look at PartialEvaluator from Matt Warren. It replaces all references to constants with the constants themselves.

Partial Evaluation of ConstantExpression in ExpressionTreeVisitor

I am not an Expression Tree master by any stretch of the imagination, what I have is code that looks like this:
int external = 10;
using(var session = new Session())
{
session.Add(new Product { Name = "test1", Price = 20 });
session.Add(new Product {Name = "test", Price = 10});
var product = session.Products.Where(p => p.Price == external).FirstOrDefault();
Assert.Equal(10, product.Price);
}
Session implements all the IQueryProvider, IQueryable interfaces you'd come to expect from a LINQ Provider.
When I evaluate the expression tree, everything goes to plan until I read the ConstantExpression for "external", at which point, I am at a loss as to how to move forward because:
//constant is ConstantExpression for "external" on the right side of the "p.Price == external" expression above.
var t = constant.GetType(); //evaluates to class called "<>c__DisplayClass2" - with a member named "external" that has the value 10.
The question is basically.. How can I just access the value of the member "external" - is there a way to accomplish this without using reflection? or am I cooked? What am I missing?
Your expression captures the external local variable, that's why an anonymous type is implicitly created by the compiler to wrap the captured variable. The right-hand side part of the equality is not a ConstantExpression, it's actually a MemberExpression, whose Expression property is a ConstantExpression of type <>c__DisplayClass2
You can access the value of the property as follows :
MemberExpression memberExpr = /* right-hand side of the equality */ as MemberExpression;
ConstantExpression constantExpr = memberExpr.Expression as ConstantExpression;
PropertyInfo prop = memberExpr.Member as PropertyInfo;
object value = prop.GetValue(constantExpr.Value, null);
I prefer something like this. It is more general and will work for a number of cases.
var operation = (BinaryExpression)predicate.Body;
var righthandValue= Expression.Lambda(operation.Right).Compile().DynamicInvoke();

Categories