I'm running into a problem that I did not expect. An example will probably illustrate my question better than a paragraph:
UPDATED: Skip to last code-block for a more eloquent code example.
public class A
{
public string B { get; set; }
}
public class C : A { }
Here is some code from a method:
var a = typeof(C).GetMember("B")[0];
var b = typeof(A).GetMember("B")[0];
Expression<Func<C, string>> c = x => x.B;
var d = (c.Body as MemberExpression).Member;
Here are the results of some comparisons:
a == b //false
a == d //false
b == d //true
The first two are somewhat unexpected. I understand that even though B is not virtual, C could define a property with the same name with thew new operator, but in this case I did not.
The second is really the most surprising to me (and is the heart of my problem). Even though the parameter for the lambda is clearly defined as being of type C, it still returns it as if the property was accessed from the base class.
What I'm looking for is a way to get the MemberInfo from a lambda expression as if I had used reflection on the type of the parameter to get the MemberInfo. My project essentially stores MemberInfos in a dictionary of sorts and it needs to have functionality where you can access the elements by providing a lambda expression.
Restated code sample by Danny Chen
public class Base
{
public string Name { get; set; }
}
public class Derived : Base { }
//in Main
var parentMember = typeof(Base).GetMember("Name")[0];
var childMember = typeof(Derived).GetMember("Name")[0];
Expression<Func<Base, string>> parentExp = x => x.Name;
var parentExpMember = (parentExp.Body as MemberExpression).Member;
Expression<Func<Derived, string>> childExp = x => x.Name;
var childExpMember = (childExp.Body as MemberExpression).Member;
parentMember == childMember //false, good
parentMember == parentExpMember //true, good
childMember == childExpMember //false, why?
Take the type of the expression's (first) parameter, and say
Expression<Func<C, string>> c = x => x.B;
Type paramType = c.Parameters[0].Type; // first parameter of expression
var d = paramType.GetMember((c.Body as MemberExpression).Member.Name)[0];
What I'm looking for is a way to get the MemberInfo from a lambda expression as if I had used reflection on the type of the parameter to get the MemberInfo.
That's not a service that expression tree conversions on lambdas were designed to provide. If you're going to use a feature "off label" then you might not get the results that you want.
The purpose of expression trees is to proffer up the compiler's semantic analysis of the expression in a form amenable to analysis at runtime, rather than compile time for the purpose of constructing query objects that can be remoted over to databases.
The compiler's correct semantic analysis is that Name is declared as a property of Base and invoked on an instance of Derived, so that's exactly the information that you get out of the resulting expression tree.
Nice question. I use some other names to make it clearer.
public class Base
{
public string Name { get; set; }
}
public class Derived : Base { }
//in Main
var parentMember = typeof(Base).GetMember("Name")[0];
var childMember = typeof(Derived).GetMember("Name")[0];
Expression<Func<Base, string>> parentExp = x => x.Name;
var parentExpMember = (parentExp.Body as MemberExpression).Member;
Expression<Func<Derived, string>> childExp = x => x.Name;
var childExpMember = (childExp.Body as MemberExpression).Member;
parentMember == childMember //false, good
parentMember == parentExpMember //true, good
childMember == childExpMember //false, why?
When debugging you will find childExpMember.ReflectedType is Base, while childMember.ReflectedType is Derived. AFAIK DeclaringType shows where the member is declared, while ReflectedType shows where the member is reflected to (because of inheritance/overriding/etc). So I think it's a bug (no official confirmation).
I believe you need to pass the flag "FlattenHierarchy" into the bindingAttr parameter of GetMember.
From MSDN:
Specifies that public and protected static members up the hierarchy should be returned. Private static members in inherited classes are not returned. Static members include fields, methods, events, and properties. Nested types are not returned.
Related
This seems simple, but I can't decipher the LINQ required to do it. I also can't add any new dependencies.
Basically, I'm trying to make this code generic:
if (filter.matchMode.ToUpper().Equals("EQ")) {
query = query.Where(x => x.SomeField.Equals(filter.Value));
if (filter.matchMode.ToUpper().Equals("LT")) {
query = query.Where(x => x.SomeField < filter.Value);
} else [... 5 other match modes ...]
}
Now, SomeField is only one of about 5 fields that needs this functionality. And there's 5 matching operators. I could just copy/pasta the whole thing, and then deal with the debt of having to change tons of code every time a new operator or field enters the mix, but that really doesn't seem right.
Basically, I need some way of defining SomeField at runtime, so I can factor out the whole if/else tree and use it for each supported field.
I've tried going down this road, but I think I'm misunderstanding something fundamental about expressions:
var entityType = typeof(TableObject);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression amproperty = Expression.Property(arg, "SomeField");
MemberExpression property = Expression.Property(amproperty, "Equals"); // ???
// what's next, if this is even the right direction...
EDIT: a shorter version of the question might be: how can I construct the following object foo using MemberExpressions and lambdas, such that SomeField can be passed in as a string "SomeField":
Expression<Func<TableObject, bool>> foo = x => x.SomeField.Equals("FOO");
UPDATE: here's what I ended up coming up with:
private IQueryable<TableObject> processFilter(
IQueryable<TableObject> query,
FilterItem filter,
string fieldName)
{
var entityType = typeof(TableObject);
// construct the argument and access object
var propertyInfo = entityType.GetProperty(fieldName);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression access = Expression.MakeMemberAccess(arg,
typeof(TableObject).GetProperty(fieldName)
);
// translate the operation into the appropriate Expression method
Expression oprFunc;
if (filter.MatchMode.ToUpper().Equals("EQ")) {
oprfunc =
Expression.Equal(access, Expression.Constant(filter.Value));
} else if (filter.MatchMode.ToUpper().Equals("LT")) {
oprfunc =
Expression.LessThan(access, Expression.Constant(filter.IntValue));
} else {
throw new ArgumentException(
$"invalid argument: ${filter.MatchMode}"
);
}
// construct the lambda
var func = Expression.Lambda<Func<TableObject, bool>>(oprFunc, arg);
// return the new query
return query.Where(func);
}
So far, this seems to cover most of the cases. It starts to go off the rails with nullable fields and date comparisons, but it will work for what I need it to do. The part I'm still not completely sure about is Expression.MakeMemberAccess. I've seen this written many ways and I'm not sure if that's the correct way to create that expression.
Let's say you have class like this:
class SomeClass
{
public string a1 { get; set; }
public string b1 { get; set; }
public string c1 { get; set; }
}
also let's assume you have method like this:
public List<T> GetAll(Expression<Func<T, bool>> criteria )
{
return someDataLINQCapable.Where(criteria);
}
Method could be called like this:
GetAll<SomeClass>(m => m.a1 != null && m.b1=="SomethingUseful")
Here's my scenario:
There is a collection of objects where each object contains a Dictionary<string, string>. The user can build a set of queries for this collection from another app to obtain a subset by selecting a Key in the Dictionary, an operator such as > or CONTAINS, etc., and a Value. They can also balance parenthesis to create groups of queries and select AND/OR operators to combine the queries.
As an example, let's say I have a collection of Car objects and the Dictionary contains keys for Make, Model, and Year.
My app is getting these queries in the form of a string like so:
"((Make = Honda) AND (Model CONTAINS Civic)) || (Year >= 2015)"
This tells me that from the collection of Car objects that I want cars that have Dictionary keys/values of <Make, Honda> and <Model, anything that contains "Civic"> OR <Year, greater than or equal to 2015>
So, I parse these out and put them into a QueryClass containing three string fields for the Key, the Operator, and the Value. I also keep track of the operator between the queries, and if they are in a group of parentheses or not.
Currently, I have to go through each QueryClass one by one performing the query, checking what the previous operator was, if it's part of a group, etc. and combining collections over and over until it reaches the end. This is tedious and seems like an awful way to do things. If there was a way to build these LINQ queries dynamically or perform SQL statements (what these are essential) on this collection it would be better.
Here's my query class that I'm storing the parsed strings in:
class QueryClass
{
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput)
{
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null)
{
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
}
My parsing class is pretty long so I won't post the whole thing but it returns a List<object> where each element is either:
A QueryClass
"AND" or "OR"
Another List which means it's a group of queries that were grouped by parentheses, containing the two choices above.
Here's an example of the List<object> I get after parsing a string:
I then just loop through each element, determine if the value is a double or string, and execute a LINQ statement on my collection. I'm checking if the operator was "AND" or "OR" (or none if it's just one query), if it's part of a group or not, and combining the results appropriately.
Here is my implementation of converting your query into a Func. Since I wasn't sure what type was in your collection, I made an interface to represent objects that had an attributes Dictionary<string, string> and processed that.
Basically I added a method to QueryClass to convert it to an Expression. It uses a helper dictionary string->lambda that builds the appropriate comparison Expression for each operator.
Then I added a class to convert the List<object> into a Func<IItem,bool> suitable for a LINQ Where filter.
public interface IItem {
Dictionary<string, string> attributes { get; set; }
}
class QueryClass {
public string FieldName { get; set; }
public string Operator { get; set; }
public object Value { get; set; }
public QueryClass(string pInput) {
var returned = RegexHelpers.SplitKeyValue(pInput); //just returns a string like "Make = Honda" into three parts
if (returned != null) {
FieldName = returned.Item1;
Operator = returned.Item2;
Value = returned.Item3;
}
}
static MethodInfo getItemMI = typeof(Dictionary<string, string>).GetMethod("get_Item");
static Dictionary<string, Func<Expression, Expression, Expression>> opTypes = new Dictionary<string, Func<Expression, Expression, Expression>> {
{ "==", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.Equal, lhs, rhs) },
{ ">=", (Expression lhs, Expression rhs) => Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, Expression.Call(lhs, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), rhs), Expression.Constant(0)) },
{ "CONTAINS", (Expression lhs, Expression rhs) => Expression.Call(lhs, typeof(String).GetMethod("Contains"), rhs) }
};
static MemberInfo attribMI = typeof(IItem).GetMember("attributes")[0];
public Expression AsExpression(ParameterExpression p) {
var dictField = Expression.MakeMemberAccess(p, attribMI);
var lhs = Expression.Call(dictField, getItemMI, Expression.Constant(FieldName));
var rhs = Expression.Constant(Value);
if (opTypes.TryGetValue(Operator, out var exprMakerFn))
return exprMakerFn(lhs, rhs);
else
throw new InvalidExpressionException($"Unrecognized operator {Operator}");
}
}
public class LinqBuilder {
static Type TItems = typeof(IItem);
static Expression BuildOneLINQ(object term, ParameterExpression parm) {
switch (term) {
case QueryClass qc: // d => d[qc.FieldName] qc.Operator qc.Value
return qc.AsExpression(parm);
case List<object> subQuery:
return BuildLINQ(subQuery, parm);
default:
throw new Exception();
}
}
static Expression BuildLINQ(List<object> query, ParameterExpression parm) {
Expression body = null;
for (int queryIndex = 0; queryIndex < query.Count; ++queryIndex) {
var term = query[queryIndex];
switch (term) {
case string op:
var rhs = BuildOneLINQ(query[++queryIndex], parm);
var eop = (op == "AND") ? ExpressionType.AndAlso : ExpressionType.OrElse;
body = Expression.MakeBinary(eop, body, rhs);
break;
default:
body = BuildOneLINQ(term, parm);
break;
}
}
return body;
}
public static Func<IItem, bool> BuildLINQ(List<object> query) {
var parm = Expression.Parameter(TItems, "i");
return Expression.Lambda<Func<IItem, bool>>(BuildLINQ(query, parm), parm).Compile();
}
}
Once you have this, you can pass in a List<object> expression and then filter your collection. Given a query q and a collection of IItems cs, you can do:
var ans = cs.Where(LinqBuilder.BuildLINQ(q));
I would approach this problem little differently, since what you are already having is List<object>, which internally contain a QueryClass containing all the relevant fields containing information, FieldName,Operator and Value, where you are aware which of the binary expressions have to be bundled in a parentheses. Important point is how can you create a Run-time Expression to take care of all kinds of scenarios.
Following is a sample to mimic your scenario:
Sample Class
public class Car
{
public string Make {get; set;}
public string Model {get; set;}
public int Year {get; set;}
}
Query
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic")) Or (c.Year >= 2015))
Linqpad Code
void Main()
{
var cars = new List<Car>();
Expression<Func<Car,bool>> e1 = c => c.Make.Equals("Honda");
Expression<Func<Car,bool>> e2 = c => c.Model.Contains("Civic");
Expression<Func<Car,bool>> e3 = c => c.Year >= 2015;
var expr1 = Expression.AndAlso(e1.Body,e2.Body);
var expr2 = e3;
var finalExpression = Expression.Or(expr1,expr2.Body);
finalExpression.Dump();
}
Purpose
As it can be seen that I have manually constructed the Expressions and finally Dump the final expression, since in Linqpad it provides a graphical representation of how shall the Expression be constructed dynamically, overall image is too big and deep to be pasted here (you may try yourself using LinqPad), but following are the relevant details:
Create a ParameterExpression, this acts as a lambda parameter representing the Car class object (this is independent of the fields of the Query class)
var parameterExpression = Expression.Parameter(typeof(Car),"c");
Create MemberExpression to access each relevant field of the Car class, which is used in the Query (this one needs Field property of the Query class)
var makeMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Make"));
var modelMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Model"));
var yearMemberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(Car).GetProperty("Year"));
Completing the Expression:
a.) c => c.Make.Equals("Honda") we create as follows: (this one needs Value property of the QueryClass)
var makeConstantExpression = Expression.Constant("Honda");
var makeEqualExpression = Expression.Equal(makeMemberAccessExpression, makeConstantExpression);
b.) c.Model.Contains("Civic") can be represented as follows here we need supply the MethodInfo for the string Contains method and create a MethodCallEXpression
var modelConstantExpression = Expression.Constant("Civic");
var stringContainsMethodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var modelContainsMethodExpression = Expression.Call(modelMemberAccessExpression, stringContainsMethodInfo, modelConstantExpression);
c.) c.Year >= 2015 can simply projected as:
var yearConstantExpression = Expression.Constant(2015);
var yearGreaterThanEqualExpression = Expression.GreaterThanOrEqual(yearMemberAccessExpression, yearConstantExpression);
Combining all together to form the Composite Expression:
Expressions a.) and b.) are combined together as follows:
((c.Make.Equals("Honda") AndAlso c.Model.Contains("Civic"))
var firstExpression = Expression.AndAlso(makeEqualExpression,modelContainsMethodExpression);
Expression c.) is independent:
c.Year >= 2015
var secondExpression = yearGreaterThanEqualExpression;
Final Combined Expression and Creation a Func Delegate
// Expressions combined via Or (||)
var finalCombinedExpression = Expression.Or(firstExpression,secondExpression);
// Create Lambda Expression
var lambda = Expression.Lambda<Func<Car,bool>>(finalCombinedExpression, parameterExpression);
// Create Func delegate via Compilation
var func = lambda.Compile();
func delegate thus can be used in any of the where clause in the Linq, which expects Func<Car,bool>
Design Suggestions
Using the explanation above and values from the Query Class placeholders or directly from the Dictionary it is feasible to create any number of Expressions to be used dynamically in the code, to be compiled and used as a Func delegate
Binary expressions like Equal, GreaterThan, LessThan, LessThanOrEqual,GreaterThanOrEqual are all exposed by the Expression trees to be used directly, For method like Contains available by default you need Reflection to get theb MethodInfo, similarly it can be done for the Static methods, just that there's object expressions
All Expressions expect values to be supplied Left to Right in the correct order, it cannot be random or incorrect order, else it will fail at run-time.
In your case, since you have few queries combined in parentheses and few independent, I would recommend creating multiple List<Expression>, where each List is combined in parentheses using AndAlso or OrElse and each list can thus be combined using And / Or
By this approach you shall be able to construct very complex requirements at runtime using Linq Expressions.
You should be able to use Linq Expressions (System.Linq.Expressions) and leverage predicates to handle your filtering.
public IQueryable<Car> GetCars(Expression<Func<Car, bool>> filter)
{
return context.Cars.Where(filter);
}
That said, the challenge will be to build your predicate expressions based off of your custom QueryClass object. To handle the filter on each Dictionary you can create a method to handle each:
public Expression<Func<Car, bool>> GetModelFilter(QueryClass modelQuery)
{
return modelQuery.Operator == "CONTAINS"? car => car.Model.Contains(modelQuery.Value) : car => car.Model == modelQuery.Value;
}
Considering you have a limited amount of filters, the above may be acceptable. However, when dealing with a large set this can also be done more dynamically, using reflection or a dynamic predicate builder but for simplicity you can follow the above.
HTH
I want to be able to build up an expression dynamically, which is essentially a property selector.
I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.
I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.
The library takes an argument of :
Expression<Func<TObject, TPropertyType>>
An example of how this would be coded if baked into an application would be :
Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;
However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.
So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.
psuedo code :
string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];
Update :
I have got as far as this
ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);
But the problem I have with this is that the return type is not always an int but may be some other type.
Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.
public static class QueryableExtension
{
public static object Build<Tobject>(this Tobject source, string propertyName)
{
var propInfo = typeof(Tobject).GetProperty(propertyName);
var parameter = Expression.Parameter(typeof(Tobject), "x");
var property = Expression.Property(parameter, propInfo);
var delegateType = typeof(Func<,>)
.MakeGenericType(typeof(Tobject), propInfo.PropertyType);
var lambda = GetExpressionLambdaMethod()
.MakeGenericMethod(delegateType)
.Invoke(null, new object[] { property, new[] { parameter } });
return lambda;
}
public static MethodInfo GetExpressionLambdaMethod()
{
return typeof(Expression)
.GetMethods()
.Where(m => m.Name == "Lambda")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 2
&& x.Args.Length == 1
)
.Select(x => x.Method)
.First();
}
}
Usage -
var expression = testObject.Build("YourPropertyName");
Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.
As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):
static LambdaExpression MakeSelector(Type objectType, string path)
{
var item = Expression.Parameter(objectType, "item");
var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
return Expression.Lambda(body, item);
}
But then you'll need to find a way to call your generic library method - using reflection or dynamic call.
If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:
public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
string property_name)
{
var parameter = Expression.Parameter(typeof (TObject));
return Expression.Lambda<Func<TObject, TPropertyType>>(
Expression.Property(parameter, property_name), parameter);
}
There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
//...........
Expression<Func<IndexViewModel, bool>> ex =
System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
var res = ex.Compile()(model);
// res == true
System.Diagnostics.Debug.Assert(res);
I have a class with quite a lot of properties and I need to post this class property names to a webservice.
Easy solution is just to create that array by hand, as in new[] {"Id", "Name", "Date", "etc"}.
But that's not fun, I would like to have intellisense support. So far I came up with creating an enum with all these properties, then having a helper function which takes array of these enums and calls .ToString() on each and adds to array.
Problem - quite an useless enum and if my class gets updated, I would need to manualy sync that enum with class properties.
Ideal solution in my mind would be to have something like LINQ extension method, where I could pass properties, something like with Select - ToPropertiesArray(x => {x.Id, X.Name, x.Date})
Am I just crazy and this cannot be done and is just plainly stupid? Or a suggestion on how to pass property names with some kind of IntelliSense support?
public class MyClass
{
public int Id{get;set;}
public string S{get;set;}
public double D{get;set;}
}
public static string[] GetPropsNamesArray<T>(Expression<Func<T,Object>> expr)
{
var t = GetObjectType(expr);
var res = t.GetProperties(BindingFlags.Instance|BindingFlags.Public)
.Select(pi => pi.Name)
.ToArray();
return res;
}
public static Type GetObjectType<T>(Expression<Func<T, object>> expr)
{
if ((expr.Body.NodeType == ExpressionType.Convert) ||
(expr.Body.NodeType == ExpressionType.ConvertChecked))
{
var unary = expr.Body as UnaryExpression;
if (unary != null)
return unary.Operand.Type;
}
return expr.Body.Type;
}
and use:
var selectedPropsNames = GetPropsNamesArray<MyClass>(m => new {m.Id,m.S});
var allPropsNames = GetPropsNamesArray<MyClass>(m => m);
As Lars said, you can use reflection. Using reflection in a method also gets you not having to rewrite when the properties collection changes. The beginning of a sample below iterates the public properties of an entity.
System.Reflection.PropertyInfo[] properties = entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (System.Reflection.PropertyInfo propertyInfo in properties)
{
// ...
}
To have Intellisense support, you can use Expressions:
public static class Helper
{
public static List<string> ToPropertiesArray(params System.Linq.Expressions.Expression<Func<object>>[] exprs)
{
return exprs.Select(expr => ((expr.Body as System.Linq.Expressions.UnaryExpression).Operand
as System.Linq.Expressions.MemberExpression).Member.Name)
.ToList();
}
}
with sample usage:
SomeClass cl = new SomeClass();
var types = Helper.ToPropertiesArray(() => cl.SomeField, () => cl.SomeOtherField);
I am trying to use Lambda Expressions in a project to map to a third party query API. So, I'm parsing the Expression tree by hand.
If I pass in a lambda expression like:
p => p.Title == "title"
everything works.
However, if my lambda expression looks like:
p => p.Title == myaspdropdown.SelectedValue
Using the .NET debugger, I don't see the actual value of that funciton. Instead I see something like:
p => p.Title = (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue)
What gives? And when I try to grab the right side of the expression as a string, I get (value(ASP.usercontrols_myaspusercontrol_ascx).myaspdropdown.SelectedValue) instead of the actual value. How do I get the actual value?
Remember that when you're dealing with the lambda expression as an expression tree, you don't have executable code. Rather you have a tree of expression elements, that make up the expression you wrote.
Charlie Calvert has a good post that discusses this in detail. Included is an example of using an expression visualiser for debugging expressions.
In your case, to get the value of the righthand side of the equality expression, you'll need to create a new lambda expression, compile it and then invoke it.
I've hacked together a quick example of this - hope it delivers what you need.
public class Class1
{
public string Selection { get; set; }
public void Sample()
{
Selection = "Example";
Example<Book, bool>(p => p.Title == Selection);
}
public void Example<T,TResult>(Expression<Func<T,TResult>> exp)
{
BinaryExpression equality = (BinaryExpression)exp.Body;
Debug.Assert(equality.NodeType == ExpressionType.Equal);
// Note that you need to know the type of the rhs of the equality
var accessorExpression = Expression.Lambda<Func<string>>(equality.Right);
Func<string> accessor = accessorExpression.Compile();
var value = accessor();
Debug.Assert(value == Selection);
}
}
public class Book
{
public string Title { get; set; }
}
To get the actual value, you need to apply the logic of the expression tree to whatever context you've got.
The whole point of expression trees is that they represent the logic as data rather than evaluating the expression. You'll need to work out what the lambda expression truly means. That may mean evaluating some parts of it against local data - you'll need to decide that for yourself. Expression trees are very powerful, but it's not a simple matter to parse and use them. (Ask anyone who's written a LINQ provider... Frans Bouma has bemoaned the difficulties several times.)
Just been struggling with exactly the same issue, thanks Bevan. On an extension, the following is a generic pattern you can use to extract the value (using this in my query engine).
[TestFixture]
public class TestClass
{
[Test]
public void TEst()
{
var user = new User {Id = 123};
var idToSearch = user.Id;
var query = Creator.CreateQuery<User>()
.Where(x => x.Id == idToSearch);
}
}
public class Query<T>
{
public Query<T> Where(Expression<Func<T, object>> filter)
{
var rightValue = GenericHelper.GetVariableValue(((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right.Type, ((BinaryExpression)((UnaryExpression)filter.Body).Operand).Right);
Console.WriteLine(rightValue);
return this;
}
}
internal class GenericHelper
{
internal static object GetVariableValue(Type variableType, Expression expression)
{
var targetMethodInfo = typeof(InvokeGeneric).GetMethod("GetVariableValue");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(variableType);
return genericTargetCall.Invoke(new InvokeGeneric(), new[] { expression });
}
}
internal class InvokeGeneric
{
public T GetVariableValue<T>(Expression expression) where T : class
{
var accessorExpression = Expression.Lambda<Func<T>>(expression);
var accessor = accessorExpression.Compile();
return accessor();
}
}
I'm not sure I understand. Where are you "seeing" that? Is that at design-time or run-time? Lambda expressions can be thought of essentially as anonymous delegates, and will operate with deferred execution. So you shouldn't expect to see the value assigned until after execution has passed that line, obviously.
I don't think that's really what you mean though... if you clarify the question a bit maybe I can help :)