Dynamic Lambda with all properties of a property of an object - c#

I had asked a question on how to dynamically compile a LINQ query with all the properties of an object and houlgap had been kind enough to give me the following code
private static Func<MyEntity, bool> GenerateLambda(MyEntity _myEntity, PropertyInfo propertyInfo)
{
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var propertyValue = Expression.Constant(propertyInfo.GetValue(_myEntity, null));
var equalityCheck = Expression.Equal(property, propertyValue);
return Expression.Lambda<Func<MyEntity, bool>>(equalityCheck, instance).Compile();
}
This works great if the property to be queried is directly a member of the object but for me there is an intermediate property in between. For e.g. The Func Delegate is for another type e.g. Func<ABCMyEntity,bool> while the MyEntity is a member of this object (ABCMyEntity.MyEntity). The Propertyinfo object which is passed is a member of MyEntity.
I know it sounds terribly confusing but I am not able to better explain it. (Maybe because I am not a native speaker). Please ask me if something is not clear in the question.
It is continued from Constructing Dynamic LINQ queries with all properties of an object

If you need to access a chain of properties (e.g. v.MyEntity.OtherProperty) then you can just call Expression.Property multiple times:
var prop1 = // first property
var prop2 = // second property
// Variable has type of the declarating type of the 1st property
var instance = Expression.Parameter(prop1.DeclaringType, "i");
// Get first property on the 'instance'
var expr1 = Expression.Property(instance, prop1);
// Get second property on the previous expression
var expr2 = Expression.Property(expr, prop2);
// The rest of the code stays the same (only use 'expr2')
var propertyValue = Expression.Constant(propertyInfo.GetValue(_myEntity, null));
var equalityCheck = Expression.Equal(expr2, propertyValue);
return Expression.Lambda<Func<MyEntity, bool>>
(equalityCheck, instance).Compile();
If you need to access more than just two properties, you can easily turn the part that calls Expression.Property into a loop - you would just iterate over a list of desired properties and add property access to the expression using Expression.Property.

Related

C# Compiled expression to create new instance of T and copy values to its properties from another instance of T

I would like to know if it is possible to create an instance of a type known only at runtime and assign values to the properties of this instance by using compiled expressions and if so how it is to be done.
I have a generic class with a method which accepts an instance of T and returns a copy. T is only known at runtime or rather is user/consumer defined. I know how to do so with reflection (assuming it has an empty constructor in this example and without exception handling or null checks for simplification).
public class MyClass<T>
{
public T CreateCopy(T source)
{
var type = typeof(T);
var copy = type.GetConstructor(Type.EmptyTypes).Invoke(null);
foreach(var pi in type.GetProperties())
{
pi.SetValue(copy, pi.GetValue(source));
}
return copy;
}
}
Reflection is quite expensive and after some digging i found an option to at least create an instance of T with compiled expressions.
var type = typeof(T);
Expression.Lambda<Func<T>>(Expression
.New(type.GetConstructor(Type.EmptyTypes)
?? throw new InvalidOperationException(
$"Type has to have an empty public constructor. {type.Name}")))
.Compile();
After some benchmarking i have found out that it performs around 6 times faster than the CreateCopy(...) method. The thing is that i do not know which type will be passed in as a generic and how many properties it will have.
Is there a way to do all of the operations from CreateCopy(...) method with compiled expressions?
Ihave looked into Expression.Asign, Expression.MemberInit but i am not able to find anything appropriate. The problem with Expression.MemberInit ist that it expects to have an Expresssion.Bind and Expression.Constant but i cant get the values of the properties from the passed instance of T into it. Is there a way?
Thank you.
P.S. So i am looking for something like:
var type = typeof(T);
var propertyInfos = type.GetProperties();
var ctor = Expression.New(type.GetConstructor(Type.EmptyTypes));
var e = Expression.Lambda<Func<T, T>>(Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, Expression.Constant(pi.GetValue(source)))))).Compile();
You are almost there. What you need is to define a parameter and then assign the properties with property access expression like below :
public static T Express<T>(T source)
{
var parameter = Expression.Parameter(typeof(T), "source");
var type = typeof(T);
var ctor = Expression
.New(type.GetConstructor(Type.EmptyTypes));
var propertyInfos = type.GetProperties();
var e = Expression
.Lambda<Func<T, T>>(
Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, CanBeAssigned(pi.PropertyType)
? (Expression)Expression.Property(parameter, pi.Name)
: Expression.Call(typeof(Program).GetMethod(nameof(Express))
.MakeGenericMethod(pi.PropertyType),
Expression.Property(parameter, pi.Name)
))
)),
parameter
);
var x = e.Compile();
var z = x(source);
return z;
}
public static bool CanBeAssigned(Type t)
{
return t.IsValueType || t.Name == "String"; // this might need some improvements
}

Can I dynamically create an expression representing a lambda which calls a method on the input parameter?

Let's say I have an object of a certain class A.
Let's say I need an Expression which calls method M on class A.
Is this even doable?
Basically, I need to programatically get this lambda
a => a.M();
The trick is, I want to do this generically, i.e. I plan to use reflection to figure out that the method is called M and what parameters it wants.
I tried using Expression.Lambda(MethodCallExpression method, ParameterExpression params).
The issue there is that when I define the method call expression, I have to specify an instance (or leave it at null if it's a static method, which it isn't). I don't want this. I want whatever the parameter is passed into the lambda (a in my example) to be the instance.
Is this possible?
Yes, it's possible to construct a linq expression at a runtime.
E.g. below is an example of constructing an expression of a method call which returns an object. This is really a dummy example as it's better to avoid object in favor of strict types.
static Expression<Func<T, object>> ComposeMethodCallExpressionAsFuncObject<T>(string methodName)
{
MethodInfo mi = typeof(T).GetMethod(methodName, types: new Type[0])
?? throw new ArgumentException($"There is no '{methodName}' method in the '{typeof(T).Name}' with the empty arguments list!");
var paramExpression = Expression.Parameter(typeof(T));
var methodCallExpression = Expression.Call(paramExpression, mi);
var result = Expression.Lambda<Func<T, object>>(methodCallExpression, paramExpression);
return result; // (T obj) =>obj.methodName()
}
, and example of usage:
int foo = 9988;
var expression = ComposeMethodCallExpressionAsFuncObject<int>(nameof(int.ToString));
//expression: (int obj) => obj.ToString()
var result = expression.Compile()(foo);
Assert.AreEqual("9988", result);

Accessing Type T property in the ParameterExpression type Dictionary<string,T>

I have expression tree code used for data filtering, till date it was used on a Generic list List<T> and following code use to work fine:
var parameterType = Expression.Parameter(typeof(T), "obj");
var memberExpression = Expression.Property(parameterType, "Name");
It was easy to create a binary expression as follows and process the result:
var constantExpression = Expression.Constant("Jack",typeof(string));
var finalExpression = Expression.Equal(memberExpression,constantExpression);
var resultFunc = Expression.Lambda<Func<T, bool>>(finalExpression, parameterType).Compile();
// Final Result
sourceList.Where(obj => resultFunc(obj));
Here Name is a property in Type T, as result post Lambda compilation was Func<T,bool>, I use to apply the same to the Where clause of IEnumerable type. Now the underlying system has changed to use the same code on Dictionary<string,T>, so all the Type T values in the collection now have a string Key associated and type T is now accessible as a value of dictionary object. Also I am applying on a IQueryable, which takes an expression tree the final lambda post compilation at source would be Func<KeyValuePair<string,T>,bool>, therefore cannot apply the Value in the final result processing.
Following is the code modification now:
var parameterType = Expression.Parameter(typeof(KeyValuePair<string,T>), "obj");
Following code fails, since now Name property is in Value of the KeyValuePair and we cannot use it as Type T:
var memberExpression = Expression.Property(parameterType, "Name");
Any pointer to make it work or any suggestion to set me in the right direction?
You could get Expression for calling ["name"] item this way:
var nameProperty= Expression.Call(parameterType,
typeof(IDictionary<string, T>).GetMethod("get_Item"),
Expression.Constant("Name"));
or as:
var nameProperty = Expression.Property(parameterType, "Item",
new Expression[] { Expression.Constant("Name") });
Both is call of Item property
EDIT : To get Value from KeyValuePair you have to get property Key, compare it with "Name" and a property Value and compare it with value:
var parameterType = Expression.Parameter(typeof(KeyValuePair<string,T>), "obj");
var value = Expression.Property(parameterType, "Value" );
var key = Expression.Property(parameterType, "Key");
var eq1 = Expression.Equal(key, Expression.Constant("Name"));
var eq2 = Expression.Equal(value, constantExpression);
var and = Expression.And(eq1, eq2);
var lambda = Expression.Lambda(and, parameterType);

How to generate a lambda at runtime passing a property name as string?

I have a list of PolicyTran objects:
List<PolicyTran> AllTransactions;
I need to run a query filtering by a property, e.g:
var insureds = AllTransactions.Select(x => x.Insured).ToList();
That works fine, but I need to pass the x.Insured property at runtime since that property could take different values.
I tried doing:
ParameterExpression x = Expression.Parameter(typeof (PolicyTran),"x");
MemberExpression body = Expression.Property(x, propertyName);
var lambda = Expression.Lambda(body,x).Compile();
var result = AllTransactions.Select(lambda).ToList();
In this case propertyName contains "Insured" or any other PolicyTran property.
But I get a compilation error saying that "The type arguments cannot be inferred by the ussage..."
I also tried, but no luck:
ParameterExpression x = Expression.Parameter(typeof (PolicyTran),"x");
var result = AllTransactions.Select(Expression.Lambda<Func<PolicyTran, bool>>(x).Compile()).ToList();
Any ideas??
Your first attempt is closer to the solution. You just need to call the generic version of Lambda:
var lambda = Expression.Lambda<Func<PolicyTran, object>>(body, x).Compile();
in order to get a Func<PolicyTran, object> delegate.
Otherwise the labda will return a simple System.Delegate from which the LINQ .Select will be unable to infer the types.

Pass a property of a Linq entity in a method to set and get result

I'm trying to pass in a property of a Linq entity to be used by my method. I can easily pass a property to be queried
Func<Entities.MyEntity, ResultType> GetProperty = ent => ent.Property;
However this returns ResultType and cannot be used to set the property.
I thought about using reflection to get a propertyInfo, but this will let me fetch the property but then I can't use Linq syntax to call my property. Is there any guru out there that knows how to do this?
I have a hunch I could do it by constructing a chunk of an expression tree and applying it onto the query...
I was really hoping to do something like:
var value = myQueryEntity.CallMagicFunction(); //typesafe
myQueryEntity.CallMagicFunction() = value; //typesafe
Indeed, an expression tree should work; for basic member access (a field/property directly off the object):
static MemberInfo ReadMember(LambdaExpression expr)
{
if(expr == null) throw new ArgumentNullException("expr");
MemberExpression me = expr.Body as MemberExpression;
if(me == null || !ReferenceEquals(me.Expression, expr.Parameters[0])) {
throw new ArgumentException("expr");
}
return me.Member;
}
with
Expression<Func<Customer, int>> func = c => c.Id;
MemberInfo member = ReadMember(func);
// for simplicity assume prop:
PropertyInfo prop = (PropertyInfo)member;
From there you can do pretty much anything; in particular you can get the get/set accessors (if you want to create a delegate), or use GetValue / SetValue.
Note that in .NET 4.0 you can set properties directly on an Expression (but the C# compiler doesn't add any extra support for this, so you'd need to write your own Expression by hand).

Categories

Resources