LINQ Expression of the Reference Property - c#

I want to get the LINQ Expression of the Reference Property
I need to get the Lambda Expression as groupCol=>groupCol.Role.Name
I have tried with the expression but not succeeded , this will work with groupCol=>groupCol.MenuText but not with reference types
var menu = Expression.Parameter(typeof(Menu), "groupCol");
// getting Role.Name' is not a member of type exception
var menuProperty = Expression.PropertyOrField(menu, property);
var lambda = Expression.Lambda<Func<Menu, string>>(menuProperty, menu);
public class Menu
{
public string MenuText {get;set;}
public Role Role {get;set;}
public string ActionName {get;set;}
}
public class Role
{
public string Name {get;set;}
}
Thanks in Advance

You need to do this one property at a time:
private static Expression<Func<Menu, string>> GetGroupKey(string property)
{
var parameter = Expression.Parameter(typeof(Menu));
Expression body = null;
foreach(var propertyName in property.Split('.'))
{
Expression instance = body;
if(body == null)
instance = parameter;
body = Expression.Property(instance, propertyName);
}
return Expression.Lambda<Func<Menu, string>>(body, parameter);
}
This answer extends the GetGroupKey method I showed you in my answer to your previous question.

Related

DynamicExpressionParser parse expression for enum property as integer

I want to create an expression for converting a property from an Enum to an Int.
I want to use DynamicExpressionParser.ParseLambda to parse nested properties:
public enum MyEnum { A, B };
public class OuterClass
{
public InnerClass Inner { get; set; }
public MyEnum Enum { get; set; }
}
public class InnerClass
{
public MyEnum Enum { get; set; }
}
DynamicExpressionParser.ParseLambda<OuterClass, int>(null, true, "Inner.Enum");
But this will throw an exception:
Expression of type 'Int32' expected (at index 0)
How can I convert the Enum to an integer inside the expression?
It works with the following method, but i cannot use nested properties there, so it will only work when I place the "Enum" property on the OuterClass:
public static Expression<Func<T, TProperty>> GetExpression<T, TProperty>(string propertyName)
{
// x =>
var parameter = Expression.Parameter(typeof(T));
// x.Name
var mapProperty = Expression.Property(parameter, propertyName);
// (object)x.Name
var convertedExpression = Expression.Convert(mapProperty, typeof(TProperty));
// x => (object)x.Name
return Expression.Lambda<Func<T, TProperty>>(convertedExpression, parameter);
}
GetExpression<OuterClass, int>("Enum");
I found a solution:
// Create expression for it-type OuterClass. Pass null for result type which will result in an expression with the result type of the actual enum type.
var enumExpression = DynamicExpressionParser.ParseLambda(typeof(OuterClass), null, "Inner.Enum");
// Convert the result type of the body to int which will result in a converted expression
var convertedBodyExpression = Expression.Convert(enumExpression.Body, typeof(int));
// Create the final expression
var expression = Expression.Lambda<Func<OuterClass, int>>(convertedBodyExpression, enumExpression.Parameters);

How to retrieve a property of an object from a string?

In order to use it as a selector in a Grouping Clause at runtime, I want basically retrieve a property from an object T by passing its name. Example with this class:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
I want to use my method like this:
private void MyMethod(string propertyName)
{
var lambda = GetLambdaExpression(propertyName);
// The problem is to specify the Type at compile time...
var selector = GetLambdaSelector<DateTime>(lambda); // Seems mandatory to convert LambdaExpression to Expression<Func<Person, T>> to use in a grouping clause
var result = emps.GroupBy(
selector.Compile(),
p => p.Birthday,
(key, g) => new { PersonName = key, = g.ToList() }).AsQueryable();
}
Here are methods statements :
LambdaExpression GetLambdaExpression(string propertyName)
{
var type = typeof(Person);
var propertyInfo = type.GetProperty(propertyName);
var propertyType = propertyInfo.PropertyType;
var entityType = propertyInfo.DeclaringType;
var parameter = Expression.Parameter(entityType, "entity");
var property = Expression.Property(parameter, propertyInfo);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyInfo.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);
return lambda;
}
Expression<Func<Person, T>> GetLambdaSelector<T>(LambdaExpression expression)
{
return (Expression<Func<Person, T>>)expression;
}
I'm stuck because I want to resolve the type (DateTimein the example) at runtime and not at compile time.
So how to call this method with type resolve at runtime:
GetLambdaSelector<?>(lambda); ?
Or maybe can I change the signature of LambdaExpression GetLambdaExpression(string propertyName) to Expression<Func<Person, T>> with reflection but I did not manage it for the moment. ?

Unboxing nullable types - workaround?

I'm storing an update operation as thus:
class Update
{
public Expression MemberExpression { get; set; }
public Type FieldType { get; set; }
public object NewValue { get; set; }
}
For example:
var myUpdate = new Update
{
MemberExpression = (MyType o) => o.LastModified,
FieldType = typeof(DateTime?),
NewValue = (DateTime?)DateTime.Now
}
Then I'm trying to apply this update later (simplified):
var lambda = myUpdate.MemberExpression as LambdaExpression;
var memberExpr = lambda.Body as MemberExpression;
var prop = memberExpr.Member as PropertyInfo;
prop.SetValue(item, myUpdate.Value);
However, myUpdate.Value is a DateTime, not a DateTime?. This is because when you cast a nullable to an object, it either becomes null or boxes the value type.
Since (DateTime?)myUpdate.Value would work to get it back to the correct type, I tried to simulate this compile-time construct using Convert.ChangeType. However, it says that casting from DateTime to DateTime? is not possible.
What approach can I use here to get the object back into its original type?
Is there any way to store a nullable types, regular structs and regular objects in a single field, and get the exact thing stored into it back again?
If you know the type of your Expression when you create myUpdate, SetInfo() will properly set your data, even if boxing/unboxing.
public class Update
{
public Expression MemberExpression { get; set; }
public Type ClassType { get; set; }
public object NewValue { get; set; }
}
public class MyType
{
public DateTime? LastModified { get; set; }
}
void Main()
{
var myUpdate = new Update
{
MemberExpression =
(Expression<Func<MyType, DateTime?>>)((MyType o) => o.LastModified),
ClassType = typeof(MyType),
NewValue = DateTime.Now
};
// At a later point where we do not want to instantiate via strong typing
var item = Activator.CreateInstance(myUpdate.ClassType);
var lambda = myUpdate.MemberExpression as LambdaExpression;
var memberExpr = lambda.Body as MemberExpression;
var prop = memberExpr.Member as PropertyInfo;
prop.SetValue(item, myUpdate.NewValue);
item.Dump();
}
This properly outputs a DateTime value corresponding to when it was created without an exception.
Maybe you can use a generic class?
class Update<TField>
{
public Expression<Func<MyType, TField>> MemberExpression { get; set; }
public TField NewValue { get; set; }
}
Not sure if you can just use a plain Func<MyType, TField> delegate instead of the expression tree, for your use.
I was able to solve using expressions and no reflection too:
var lambda = update.MemberExpression as LambdaExpression;
var memberExpr = lambda.Body as MemberExpression;
if (memberExpr == null)
{
throw new NotSupportedException("Field expression must be a member expression");
}
// do a cast - this ensures nullable types work, for instance
var cast = Expression.Convert(Expression.Constant(update.Value), update.FieldType);
// assign the target member with the cast value
var assignment = Expression.Assign(memberExpr, cast);
// build a new lambda, no return type, which does the assignment
var newLambda = Expression.Lambda(typeof(Action<T>), assignment, lambda.Parameters[0]);
// compile to something we can invoke, and invoke
var compiled = (Action<T>)newLambda.Compile();
compiled(item);
And voila, the item is modified :)

Get MemberInfo of child property from a MemberExpression

I am trying to get MemberInfo for a child property from a MemberExpression. I have found ways to get the full name of the nested type, but not a way to get the whole MemberInfo of the nested type. Here is a quick example of the scenario I am talking about:
Some simple models (the goal is to eventually get the MemberInfo of the Data property of the Child class)
public class Parent
{
public int ParentProperty { get; set; }
public Child Child { get; set; }
}
public class Child
{
public string Data { get; set; }
}
The lambda expression
Expression<Func<Parent,string>> func = new Func<Parent, string>(p =>
{
return p.Child.Data;
});
Code used to get the MemberInfo from the lambda expression.
internal static MemberInfo FindMemberInfoFromLambda(LambdaExpression lambda)
{
var expression = (Expression) lambda;
var flag = false;
while (!flag)
{
switch (expression.NodeType)
{
case ExpressionType.Convert:
expression = ((UnaryExpression) expression).Operand;
continue;
case ExpressionType.Lambda:
expression = ((LambdaExpression) expression).Body;
continue;
case ExpressionType.MemberAccess:
var memberExpression = (MemberExpression) expression;
if (memberExpression.Expression.NodeType == ExpressionType.Parameter ||
memberExpression.Expression.NodeType == ExpressionType.Convert)
return memberExpression.Member;
throw new Exception();
default:
flag = true;
continue;
}
}
throw new Exception();
}
This code works great if I were trying to get the ParentProperty of the Parent class, but when I try to get the MemberInfo of the Data property of the Child class, it does not work. I have seen a few StackOverflow questions posted on getting the full name of the child property, but nothing on getting the whole MemberInfo of it. Has anyone done this before or can help point me in the right direction?
The expression you get is MemberExpression, you can grab its Member property directly:
class Program
{
class Parent
{
public int A { get; set; }
public Child Child { get; set; }
}
class Child
{
public string Data { get; set; }
}
public static MemberInfo GetMemberInfo(LambdaExpression exp)
{
var body = exp.Body as MemberExpression;
return body.Member;
}
static void Main(string[] args)
{
Expression<Func<Parent, string>> func1 = p => p.Child.Data;
Console.WriteLine(GetMemberInfo(func1));
Expression<Func<Parent, int>> func2 = p => p.A;
Console.WriteLine(GetMemberInfo(func2));
}
}
Output:
System.String Data
Int32 A
You must be using Expression instead of just Func
In your code in the MemberAccess section you are checking if the member is from the parameter, in this case Parent. If you remove that check then you will get the member for Data
Change this section
case ExpressionType.MemberAccess:
var memberExpression = (MemberExpression) expression;
if (memberExpression.Expression.NodeType == ExpressionType.Parameter ||
memberExpression.Expression.NodeType == ExpressionType.Convert)
return memberExpression.Member;
throw new Exception();
To
case ExpressionType.MemberAccess:
var memberExpression = (MemberExpression) expression;
return memberExpression.Member;
I don't know why you had the guard for the parameter, if you need it in certain cases then you can create a different method or pass in a parameter.

Dynamic recursive lambda expressions

I want to create dynamic lambda expressions so that I can filter a list using a set of filtering parameters. This is what I have so far:
The expression is built using the methods bellow, where T is the object type of the list
public static Expression<Func<T, bool>> GetExpression<T>(IList<DynamicFilter> filters)
{
if (filters.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
Expression exp = null;
if (filters.Count == 1)
exp = GetExpression<T>(param, filters[0]);
[...]
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, DynamicFilter filter)
{
MemberExpression member = Expression.Property(param, filter.PropertyName);
ConstantExpression constant = Expression.Constant(filter.Value);
[...]
return Expression.Call(member, filterMethod, constant);
}
I then call
List<Example> list = ...;
var deleg = ExpressionBuilder.GetExpression<Example>(dynFiltersList).Compile();
list = list.Where(deleg).ToList();
This works just as expected with an object that contains only simple types, but if there are complex types inside, the code doesn't work anymore. For example, let's say I have a member of custom type Field inside the Example class and Field has a string property Value. If filter.PropertyName would be 'Field0' (of type Field), the code would work just fine, but if I have 'Field0.Value' I would get an obvious error stating that there is no property named 'Field0.Value' inside class Example.
I tried modifying the expression building method, like this:
MemberExpression member = null;
if (filter.PropertyName.Contains('.'))
{
string[] props = filter.PropertyName.Split('.');
ParameterExpression param1 = Expression.Parameter(typeof(T).GetProperty(props[0]).PropertyType, "t1");
member = Expression.Property(param1, props[0]);
}
else
{
member = Expression.Property(param, filter.PropertyName);
}
but then I got a Lambda parameter not in scope error when compiling the expression. I sort of understand why I get this error, but I don't know how to make this work.
Bottom line is I need to make the expression building method work recursively when forming the MemberExpression. I ultimately need to obtain a list = list.Where(deleg).ToList(); that translates to something like this list = list.Where(obj => obj.Field0.Value == 'something').ToList();
I've just started working with expressions so I don't really know too much in this area, but any help would be appreciated.
Thanks
I realize this is a fairly old post, but I had the exact same problem and found something close in an answer that Mark Gravell posted here. I just modified it slightly to meet my needs and below is the result:
private Expression GetDeepProperty(Expression parameter, string property)
{
var props = property.Split('.');
var type = parameter.Type;
var expr = parameter;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
return expr;
}
Implementation:
var method = typeof (string).GetMethod("Contains", new Type[] {typeof (string)}, null);
var lambdaParameter = Expression.Parameter(typeof(TEntity), "te");
var filterExpression = Expression.Lambda<Func<TEntity, bool>> (
filters.Select(filter => Expression.Call(GetDeepProperty(lambdaParameter, filter.Property),
method,
Expression.Constant(filter.Value))).
Where(exp => exp != null).
Cast<Expression>().
ToList().
Aggregate(Expression.Or), lambdaParameter);
I'm trying to address
so that I can filter a list using a set of filtering parameters
not by using ExpressionBuilder but instead by using a generic Filter class.
public class Filter<T> where T: class
{
private readonly Predicate<T> criteria;
public Filter(Predicate<T> criteria)
{
this.criteria = criteria;
}
public bool IsSatisfied(T obj)
{
return criteria(obj);
}
}
First we need to have some classes.
public class Player
{
public string Name { get; set; }
public int Level { get; set; }
public enum Sex { Male, Female, Other };
public Weapon Weapon { get; set; }
}
public class Weapon
{
public string Name { get; set; }
public int MaxDamage { get; set; }
public int Range { get; set; }
public WeaponClass Class { get; set; }
public enum WeaponClass { Sword, Club, Bow }
}
Then we need a list of objects.
var graywand = new Weapon { Name = "Graywand", MaxDamage = 42, Range = 1, Class = Weapon.WeaponClass.Sword };
var scalpel = new Weapon { Name = "Scalpel", MaxDamage = 33, Range = 1, Class = Weapon.WeaponClass.Sword };
var players = new List<Player> {
new Player { Name = "Fafhrd", Level = 19, Weapon = graywand },
new Player { Name = "Gray Mouser", Level = 19, Weapon = scalpel },
new Player { Name = "Freddy", Level = 9, Weapon = graywand },
new Player { Name = "Mouse", Level = 8, Weapon = scalpel}
};
Then let's create a couple of filters and add those to a list.
var powerfulSwords = new Filter<Player>(p => p.Weapon.MaxDamage>35);
var highLevels = new Filter<Player>(p => p.Level>15);
var filters = new List<Filter<Player>>();
filters.Add(powerfulSwords);
filters.Add(highLevels);
Finally filter the list by those filters
var highLevelAndPowerfulSwords = players.Where(p => filters.All(filter => filter.IsSatisfied(p)));
var highLevelOrPowerfulSwords = players.Where(p => filters.Any(filter => filter.IsSatisfied(p)));
Only "Fafhrd" will be in highLevelAndPowerfulSwords and highLevelOrPowerfulSwords will contain all players but "Mouse".
Have a look at ExpressionVisitor as described here: Replacing the parameter name in the Body of an Expression
You could generate an expression for each element in filter and combine them into one single expression using the methods below:
public static Expression<Func<T, K>> CombineAnd<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
{
ParameterExpression firstParameter = a.Parameters.First();
Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
return Expression.Lambda<Func<T, K>>(Expression.And(a.Body, b1.Body), firstParameter);
}
public static Expression<Func<T, K>> CombineOr<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
{
ParameterExpression firstParameter = a.Parameters.First();
Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
return Expression.Lambda<Func<T, K>>(Expression.Or(a.Body, b1.Body), firstParameter);
}

Categories