This question already has an answer here:
-
Convert Linq expression “obj => obj.Prop” into “parent => parent.obj.Prop”
1 answer
Is it possible to create an Expression
that receives a type X
from an Expression
that receives a type Y
if X
contains Y
inside it?
For example, I have these types:
public class Y {
public int Something { get; set; }
}
public class X {
public Y Y { get; set; }
}
Let's say I have this function that returns an Y
Expression
:
private static Expression<Func<Y, bool>> ExprY(Y y2)
{
return y1 => y1.Something == y2.Something;
}
As you can see, I can send a Y
object to this function and it will return an Expression<Func<Y, bool>>
.
Now, let's say that I want to create the same Expression
but from X
, that way I can write this:
private static Expression<Func<X, bool>> ExprX(X x2)
{
return x1 => x1.Y.Something == x2.Y.Something;
}
This works fine, but I am duplicating code since the same logic to compare Something
is inside both functions, so the question is how to rewrite ExprX
to somehow use ExprY
inside itself so I don't need to repeat the Something
comparison?
Something like that:
public static Expression<Func<X, bool>> ExprX(X x2)
{
return x1 => ExprY(x2.Y)(x1.Y);
}
PS. this is intended to be code run by Entity Framework 6 in a .Where
clause, so the answer needs to work with that framework.
You can use ExpressionVisitor
to replace all Y
in the expression with x.Y
and build the new expression.
class ReplaceParameterExpressionVisitor : ExpressionVisitor
{
private readonly Expression _target, _replacement;
public ReplaceParameterExpressionVisitor(ParameterExpression target, Expression replacement)
{
_target = target;
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Equals(_target) ? _replacement : node;
}
}
The visitor is very simple, find the target ParameterExpression
and replace it with the _replacement
expression.
public static class ExpressionExtensions
{
public static Expression<Func<T, R>> ReplaceParameter<T, R, U>(this Expression<Func<U, R>> origin, Expression<Func<T, U>> accessor)
{
var originalParameter = origin.Parameters.Single();
var replacement = accessor.Body;
var visitor = new ReplaceParameterExpressionVisitor(originalParameter, replacement);
var newBody = visitor.Visit(origin.Body);
return Expression.Lambda<Func<T, R>>(newBody, accessor.Parameters.Single());
}
}
The above extension method will replace the parameters in the body using the visitor, get the new body, and build a new lambda expression with the new parameter.
class User
{
public string Name { get; set; }
}
Expression<Func<string, bool>> exp = s => s.Equals("Jack");
Expression<Func<User, string>> nameAccessor = u => u.Name;
var newExp = exp.ReplaceParameter(nameAccessor); // u => u.Name.Equals("Jack")