Search code examples
c#expressionexpression-treesmongodb-.net-driverexpressionvisitor

Create expression tree on parent object from existing expression for c# mongodb driver parsing


I have a class like this :

public class Person
{
    public string Name { get; set; }

    public string Email { get; set; }

    // ...
}

And I have an other generic class like :

public class MyParentObject<T>
{

    public T Item { get; set; }

    // ...
}

I have for example an expression like this :

Expression<Func<Person, bool>> expression = p => p.Name == "MyName" && p.Email = "[email protected]"

How can I transform this expression to :

Expression<Func<MyParentObject<Person>, bool>> expression = p => p.Item.Name == "MyName" && p.Item.Email = "[email protected]"

EDIT :

I think my question wasn't enough develop, I would like a method like this :

public static Expression<Func<MyParentObject<T>, bool>> GetParentExpression(Expression<Func<T, bool>> expression)
{
    Expression <Func<MyParentObject<T>, bool>> result = // process...
    // ...
    return result;
}

EDIT :

I must create a new tree expression which will be parsed then by c# mongodb driver. I can't use Invoke an other Compile methods on expression tree.

I think I must use ExpressionVisitor but I don't know how...


Solution

  • You can use Expression.Invoke for that like this

    public static Expression<Func<MyParentObject<T>, bool>> GetParentExpression<T>(Expression<Func<T, bool>> expression)
    {
        Expression<Func<MyParentObject<T>, T>> item = parent => parent.Item;
        var result = Expression.Lambda<Func<MyParentObject<T>, bool>>(
            Expression.Invoke(expression, item.Body), item.Parameters);
        return result;
    }
    

    Update: Another way which requires a bit more code, but eliminates the Expression.Invoke is to use a ExpressionVisitor derived class to replace the parameter of the passed expression like this

    public static Expression<Func<MyParentObject<T>, bool>> GetParentExpression<T>(Expression<Func<T, bool>> expression)
    {
        Expression<Func<MyParentObject<T>, T>> item = parent => parent.Item;
        var body = new ParameterExpressionReplacer { source = expression.Parameters[0], target = item.Body }.Visit(expression.Body);
        var result = Expression.Lambda<Func<MyParentObject<T>, bool>>(body, item.Parameters);
        return result;
    }
    
    class ParameterExpressionReplacer : ExpressionVisitor
    {
        public ParameterExpression source;
        public Expression target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == source ? target : base.VisitParameter(node);
        }
    }