Use value of property expression inside expression tree - c#

Consider a property expression like t => t.MyProperty where t is of type MyClass. How can I use this property expression inside a new expression where I perform a method call?
Pure C#:
class MyClass
{
public string MyProperty { get; set; }
}
static void Foo(string foo)
{
}
LambdaExpression GetExpression(Expression<Func<MyClass, object>> expr)
{
return expr;
}
var myClass = new MyClass();
Foo(myClass.MyProperty);
Now with expressions...?
var expr = GetExpression(m => m.MyProperty);
var mi = typeof(Program).GetMethod(nameof(Program.Foo),
BindingFlags.Public | BindingFlags.Static);
var myClass = new MyClass();
// Now what??
// var call = Expression.Call(mi, ???expr??);
// var invoke = Expression.Invoke(call, fooParameter);
I want to use the result of expr and use that in the call to Foo. I know I can do this in two steps, where I call expr.Compile().DynamicInvoke(myClass) to get the value, but that is not what I'm asking for here.
I want to build an expression that takes a property getter expression and then performs a call to Foo(result of expression). I cannot figure out how to use the expression as a parameter to the method call.

There's two ways of doing this, depending on the complexity. In this case, we can re-use the parameter from the inner expression - bubbling it outwards; we do this by discarding our old lambda, just using the .Body and .Parameters. For example:
var call = Expression.Lambda<Action<MyClass>>(
Expression.Call(mi, expr.Body), expr.Parameters);
var myClass = new MyClass { MyProperty = "yay!" };
call.Compile().Invoke(myClass);
The other way is to use Invoke on the inner lambda:
var outerParameter = Expression.Parameter(typeof(MyClass));
var typed = Expression.Convert(Expression.Invoke(expr, outerParameter), typeof(string));
var call = Expression.Lambda<Action<MyClass>>(Expression.Call(mi, typed), outerParameter);
The second form (Invoke) is useful when you can't conveniently control the parameters in the two places - or where, for example, you have multiple inner expressions with different parameter instances.

Related

Dynamically creating an expression which selects an objects property

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

How to assign a value from MemberExpression to a field according to another MemberExpression?

I would like write a method which accepts two MemberExpression, and generates a delegate which accepts two objects - source and target, and assigns the value from the source - according to it's MemberExpression, to the field of the target, according to the second MemberExpression. The objects does not have to be of the same type.
I'm looking for something like this:
public Action<TSource, TTarget> Map(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
/*
* Now I would like to create a lambda expression which accepts TSource and TTarget instances,
* and assings TTarget according to the above getter and setter expressions. Kind of like:
* var assignExp = Expression.Assign(x, y);
* var lambda = Expression.Lambda<Action<TTarget, TSource>>( .... ).Compile();
* return lambda;
*/
}
Usage:
Target target;
Source source;
//...
var action = Map(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
I don't understand how to build the expressions to send to Expression.Assign.
At this point, I don't mind about null values or initialization of fields. Please assume all fields are initialized.
Assign is used to generate assign expression, but in your case each lambda expression has own parameter, and both this parameters should be send to a new lambda expression.
So in my example i generate new assign expression, then create a new lambda expression, and send ParameterExpression from both getter and setter expressions to a new lambda.
So it should be like this:
Here is working sample - https://dotnetfiddle.net/uuPVAl and the code itself
using System;
using System.Linq.Expressions;
public class Program
{
public static void Main(string[] args)
{
Target target = new Target();
Source source = new Source()
{
NestedField = new NestedSource()
{
Dummy = "Hello world"
}
};
var action = Map<Source, Target>(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);
Console.WriteLine(target.TargetName);
}
public static Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var sourceField = getter.Body as MemberExpression;
var targetField = setter.Body as MemberExpression;
// here we create new assign expression
var assign = Expression.Assign(targetField, sourceField);
// and then compile it with original two parameters
var lambda = Expression.Lambda<Action<TSource, TTarget>>(assign, getter.Parameters[0], setter.Parameters[0]);
return lambda.Compile();
}
}
public class Target
{
public string TargetName { get; set; }
}
public class NestedSource
{
public string Dummy { get; set; }
}
public class Source
{
public NestedSource NestedField { get; set; }
}
UPDATE
So each Lambda Expression can have any parameters. From code side it's ParameterExpression. When you write expression as typical code, then it means function parameters, so in your case (p) => p.NestedField.Dummy - (p) is parameter of that function. And expression inside body uses it - p.NestedField.Dummy, so to be able to compile it - lambda expression needs to know that parameter.
In this case you have two lambda expressions for target and source, and each of them have own parameter - (p) and (x) and each expression use own parameter. But in result function we need to use both of them, as we have two parameters in the function, so we need to resend original ParameterExpression from source and target to a new lambda. Or you can create a new ParameterExpression but then you need to create a new tree as old one will use old ParameterExpression. Usually such things are done with ExpressionVisitor class, but in your case we can just resend original expressions without tree body changes.
This will do:
public Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
var targetPropertyExpression = setter.Body as MemberExpression;
var targetProperty = targetPropertyExpression.Member as PropertyInfo;
return (src, tgt) => { targetProperty.SetValue(tgt, getter.Compile().Invoke(src)); };
}
It get's the property of the setter from the 1st lambda expression and just returns an action, which assigns the value to the property based on the 2nd lambda expression, which just needs to be invoked.
Take care of the <TSource, object> though, you maybe need an additional cast.

Multiple Groupby keySelectors using Linq Expressions

I have a list of objects which need to be grouped by a set of properties. These set of properties would be known only at runtime . I would like to accomplish this without using Dynamic LINQ. Following code from Dynamically Adding a GroupBy to a Lambda Expression by Kirk Woll works fine, but this groups the result by one property.
public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(IQueryable<T> source, string column)
{
PropertyInfo columnProperty = typeof(T).GetProperty(column);
var sourceParm = Expression.Parameter(typeof(T), "x");
var propertyReference = Expression.Property(sourceParm, columnProperty);
var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);
return source.GroupBy(groupBySelector);
}
I would like to pass a list of columnNames and be able to groupby multiple keySelectors. I am not sure how to accomplish this. Please help.
It seems like if we could dynamically generate the equivalent of the expression:
x => new { a = x.Prop1, b = x.Prop2, ... }
Then a reasonable LINQ => SQL provider would probably generate the SQL you want. Generating a new anonymous type on the fly seems hard, but we could take advantage of the fact that anonymous types are generic to re-use one.
// at the top of your class
private static readonly object AnonymousObjectWithLotsOfProperties = new {
a = 1,
b = 2,
...
};
// then in your method
// (1) first get the list of properties represented by the string you passed in (I assume you know how to do this via reflection)
var props = typeof(T).GetProperties().Where(...);
var propTypes = props.Select(pi => pi.PropertyType).ToList();
// (2) now generate the correctly genericized anonymous type to use
var genericTupleType = AnonymousObjectWithLotsOfProperties.GetType()
.GetGenericTypeDefinition();
// for generic args, use the prop types and pad with int
var genericArguments = propTypes.Concat(Enumerable.Repeat(typeof(int), genericTupleType.GetProperties().Length - propTypes.Count))
.ToArray();
var tupleType = genericTupleType.MakeGenericType(genericArguments);
// (3) now we have to generate the x => new { ... } expression
// if you inspect "System.Linq.Expressions.Expression<Func<object>> exp = () => new { a = 2, b = 3 };"
// in the VS debugger, you can see that this is actually a call to a constructor
// on the anonymous type with 1 argument per property
var tParameter = Expression.Parameter(typeof(T));
// create the expression
var newExpression = Expression.New(
constructor: tupleType.GetConstructors().Single(), // get the one constructor we need
// the arguments are member accesses on the parameter of type T, padded with 0
arguments: props.Select(pi => Expression.MakeMemberAccess(tParameter, pi))
.Concat(Enumerable.Repeat(Expression.Constant(0), genericTupleType.GetProperties().Length - propTypes.Count))
);
// create the lambda: we need an Expression<TDelegate>, which means that we
// need to get the generic factory method from Expression and invoke it
var lambdaGenericMethod = typeof(Expression).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(m => m.IsGenericMethodDefinition);
var lambdaMethod = lambdaGenericMethod.MakeGenericMethod(typeof(Func<,>).MakeGenericType(typeof(T), tupleType));
// reflection for Expression.Lambda(body, parameters)
var lambda = lambdaGenericMethod.Invoke(null, new object[] { newExpression, new[] { tParameter });
// now that we have the expression, we can invoke GroupBy via reflection.
// Of course, that leaves you with an IQueryable<IGrouping<ANON, T>>, which isn't much
// use until you apply some other IQueryable methods to eliminate the ANON type from the
// method signature so you can return it
Note that I didn't actually get to compile and run this code, so I can't promise that everything above is perfect. However, hopefully it will put you on the right track.
What's wrong with this:
Group By Multiple Columns
Here's an example using the .GroupBy method rather than the Linq:
public class Program
{
static void Main(string[] args)
{
var list = new List<SomeType>
{
new SomeType("Mr", "Joe", "Bloggs"),
new SomeType("Mr", "John", "Citizen"),
new SomeType("Mrs", "Mary", "Mac")
};
var list2 = list.GroupBy(by => new { by.Property1, by.Property2 });
}
}
public class SomeType
{
public string Property1 { get; private set; }
public string Property2 { get; private set; }
public string Property3 { get; private set; }
public SomeType(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
}
}

Sophisticated builder methods with Expression trees

Let's say I've to create the following method in runtime
Type someType = ...; // assigned somewhere
var method = (presenter) =>
{
var instance = new SomeType(); // I've only default constructor here
instance.SomeProperty = presenter;
return instance;
}
Does anybody know how can I create such method in runtime using Expression trees?
The key here is to use Expression.Block to get the multi statement lambda. You can use something like:
void Main()
{
var p = Expression.Parameter(typeof(int), "presenter");
var instance = Expression.Variable(typeof(Foo), "instance");
var ctor = typeof(Foo).GetConstructor(new Type[0]);
var block = Expression.Block(new [] { instance },
Expression.Assign(instance, Expression.New(ctor)),
Expression.Assign(Expression.Property(instance, "SomeProperty"), p),
instance
);
var lambda = Expression.Lambda<Func<int,Foo>>(block, p);
Foo f = lambda.Compile()(5);
Console.WriteLine(f.SomeProperty);
}
class Foo
{
public int SomeProperty {get;set;}
}
Of course you need to tweak this to fit your needs, which parts are variable and which are not, and so on, but it demonstrates the general idea.

Lambda Expression: cast parameter to base type

I'd like to "wrap" the getter function for a specific property that is part of a specific type.
I have an abstract class, defined like following:
public abstract class MyAbstractClass<T> where T : MyType
{
// ...
}
Well, suppose I've a concrete class like the following one:
public abstract class MyConcreteClass : MyAbstractClass<MyConcreteType>
{
// ...
}
And now, the helper method that should return a wrapper for the getter method:
private Func<MyAbstractClass<T>, Object> GetPropertyGetter(PropertyInfo property)
{
var instanceType = Expression.Parameter(property.DeclaringType, "i");
// Taking getter's "body".
var getterBody = Expression.Property(instanceType, property);
// Cast to a generic Object.
var body = Expression.TypeAs(getterBody, typeof(Object));
// Build the expression.
var exp = Expression.Lambda<Func<MyAbstractClass<T>, Object>>(body, instanceType);
return exp.Compile();
}
Just like expected, I get the following exception:
ParameterExpression of type 'MyConcreteClass' cannot be used for
delegate parameter of type 'MyAbstractClass<MyConcreteType>'.
Is there a way to "force" this kind of casting? Thanks in advance.
If I understand your question correctly, you want to create a lambda expression like this:
Func<MyAbstractClass<T>, Object> f = i => ((MyConcreteClass)i).SomeProperty;
Except you want to provide which property is SomeProperty as a parameter. Well, if you want to build that expression programmatically, you have to do exactly the same: have expression parameter i (of type MyAbstractClass<T>), cast it to MyConcreteClass and then access property SomeProperty of that.
public Func<MyAbstractClass<T>, Object> GetPropertyGetter(PropertyInfo property)
{
var parameter = Expression.Parameter(typeof(MyAbstractClass<T>), "i");
var cast = Expression.TypeAs(parameter, property.DeclaringType);
var getterBody = Expression.Property(cast, property);
var exp = Expression.Lambda<Func<MyAbstractClass<T>, Object>>(
getterBody, parameter);
return exp.Compile();
}
Having said that, I have absolutely no idea why would you want to do that, so you better be really sure this is what you want to do and that there isn't any better way of doing what you actually want to do.
Shouldn't you be building the instance type from the generic type?
var genericType = typeof(MyAbstractClass<>).MakeGenericType(property.DeclaringType);
var instanceType = Expression.Parameter(genericType , "i");

Categories