修改包装类的谓词表达式

时间:2017-02-05 10:15:35

标签: c# linq linq-expressions

我有一个存储库类,允许使用lambda表达式进行查询(简化的部分代码):

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

但是,对于特定的提供者,我需要将原始对象包装在另一个类中:

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

所以在这种情况下,我还需要将传入的谓词表达式包装在另一个表达式中:

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();
    }
}

例如x => x.ParentId == 123应该变为x => x.OriginalObject.ParentId == 123

我无法找到这种情况的例子,而且我自己很难解决这个问题。如何使用OriginalObject属性预先添加谓词表达式?

1 个答案:

答案 0 :(得分:4)

回答具体问题。

给出表达式

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

转换表达式

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

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

是用oe2的正文)替换w.OriginalObject正文中的e1参数出现的问题。像字符串替换,但表达式:)

首先,您需要一个用其他方法替换表达式参数的方法。这是我使用的那个:

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);
        }
    }
}

现在问题的方法可能是这样的:

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);
    }
}

和用法

var wrappedFilter = filter.ToWrapped();