Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying predicate expression for wrapping class

I have a repository class that allows for queries using a lambda expression (simplified partial code):

public class SomeRepository<T>: IRepository<T> 
{
    public IList<T> Find(Expression<Func<T, bool>> filter)
    {
        return SomeQueryProvider.Where(filter).ToList();
    }
}    

However, for a specific provider I have the need to wrap the original object in another class:

public class Wrapped<T>
{
    public T OriginalObject { get; set; }
}

So in this case, I also need to wrap the incoming predicate expression in another expression:

public class AnotherRepository<T>: IRepository<T> 
{
    public IList<T> Find(Expression<Func<T, bool>> filter)
    {
        Expression<Func<Wrapped<T>, bool>> wrappedFilter = ...
        return AnotherQueryProvider.Where(wrappedFilter).ToList();
    }
}

For example x => x.ParentId == 123 should become x => x.OriginalObject.ParentId == 123.

I can't find examples for this scenario, and I'm having difficulty solving this myself. How can I prepend the predicate expression with the OriginalObject property?

like image 924
Carvellis Avatar asked Jul 19 '26 21:07

Carvellis


1 Answers

Answering the concrete question.

Given expression

Expression<Func<Wrapped<T>, T>> e1 = w => w.OriginalObject;

converting the expression

Expression<Func<T, bool>> e2 = o => o.ParentId == 123;

to

Expression<Func<Wrapped<T>, T>> e3 = w => w.OriginalObject.ParentId == 123;

is a matter of replacing the o parameter occurrences inside the e2 body with w.OriginalObject (the body of the e1). Something like string replace, but for expressions :)

First you need a method that replaces expression parameter with something else. Here is the one that I use:

public static partial class ExpressionUtils
{ 
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }
    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

Now the method in question could be like this:

partial class ExpressionUtils
{
    public static Expression<Func<Wrapped<T>, TResult>> ToWrapped<T, TResult>(this Expression<Func<T, TResult>> source)
    {
        Expression<Func<Wrapped<T>, T>> unwrap = w => w.OriginalObject;
        var parameter = unwrap.Parameters[0];
        var body = source.Body.ReplaceParameter(source.Parameters[0], unwrap.Body);
        return Expression.Lambda<Func<Wrapped<T>, TResult>>(body, parameter);
    }
}

and the usage

var wrappedFilter = filter.ToWrapped();
like image 107
Ivan Stoev Avatar answered Jul 22 '26 11:07

Ivan Stoev