I have a method like this:
private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
/* Some code that will call Method_2 */
}
In this method I want to change the IPerson
type to another type. I want to call another method that looks like this:
private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
/* Some code */
}
So, in method_1
I need to change IPerson
to PersonData
. How can I do this?
Edit:
When I call: Method_1(p => p.Id == 1)
I want to 'save' the condition (p.Id == 1
) but I want to execute this condition on another type, namely IPerson
. So, I need to alter the expression or create a new expression with IPerson
Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.
That's the basics of building an expression tree in memory. More complex trees generally mean more node types, and more nodes in the tree. Let's run through one more example and show two more node types that you will typically build when you create expression trees: the argument nodes, and method call nodes.
Expression trees that represent lambda expressions are of type LambdaExpression or Expression<TDelegate>. To execute these expression trees, call the Compile method to create an executable delegate, and then invoke the delegate.
It is easy if you use .net 4 (update: as noted in comment ExpressionVisitor
was added in version 4 not 4.5) it would require some googling for older frameworks:
There are some assumptions but I think they are valid for your DTO and Entity scenario - properties accessed must match.
class PersonData
{
public bool Prop { get; set; }
}
interface IPerson
{
bool Prop { get; set; }
}
In .net 4 there is ExpressionVisitor
class defined that makes this a lot easier if you use older one then you need to write or find implementation of it:
class Visitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
//only properties are allowed if you use fields then you need to extend
// this method to handle them
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
//name of a member referenced in original expression in your
//sample Id in mine Prop
var memberName = node.Member.Name;
//find property on type T (=PersonData) by name
var otherMember = typeof(T).GetProperty(memberName);
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
}
Proof of concept:
class Program
{
static void Main()
{
//sample expression
Expression<Func<IPerson, bool>> expression = x => x.Prop;
//parameter that will be used in generated expression
var param = Expression.Parameter(typeof(PersonData));
//visiting body of original expression that gives us body of the new expression
var body = new Visitor<PersonData>(param).Visit(expression.Body);
//generating lambda expression form body and parameter
//notice that this is what you need to invoke the Method_2
Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
//compilation and execution of generated method just to prove that it works
var boolValue = lambda.Compile()(new PersonData());
}
}
Note that this will work for simple expressions. If you need to handle x.Prop.Prop1 < 3
then you need to extend this further.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With