将一个 Expression<Func<T,bool>> 转换为 Expression<Func<T1,bool>>,使得 T 是 T1 的成员。

5
我们有一个类型为T1的实体,其中有一个类型为T的成员。类似这样:
public class T1
{
    public T Member{get;set;}
}

用户可以使用我们的UI来过滤T,我们将其翻译为一个函数表达式,该表达式接受T并返回bool (Expression<Func<T,bool>>) 我想知道是否可能将其转换为一个接受T1并返回bool的函数表达式。
实际上,我想要将这个转换成:
(t=>t.Member1==someValue && t.Member2==someOtherValue);

转化为:

(t1=>t1.Member.Member1==someValue && t1.Member.Member2==someOtherValue);

我假设编译器静态地没有这些信息。我知道T->bool表达式是动态提供的,但是T1和Member是否是静态可用的?你还需要将其作为表达式吗,还是可以将其转换为委托? - Lasse V. Karlsen
你可以尝试使用Invoke - Grundy
因为我必须将它传递给 Linq 提供程序并且必须使用 IQueryables 进行操作,所以我认为我必须将它们作为表达式? - Beatles1692
"AsExpandable"可以做到这一点:http://www.albahari.com/nutshell/linqkit.aspx - usr
1
呵呵,我喜欢在其他问题中发布我的答案,我认为这也可以帮助你 :P https://dev59.com/0Inca4cB1Zd3GeqP-GDO#29471092 - MBoros
显示剩余6条评论
2个回答

3

鉴于

public class MyClass
{
    public MyInner Member { get; set; }
}

public class MyInner
{
    public string Member1 { get; set; }
    public string Member2 { get; set; }
}

加号

public static Expression<Func<TOuter, bool>> Replace<TOuter, TInner>(Expression<Func<TInner, bool>> exp, Expression<Func<TOuter, TInner>> outerToInner)
{
    var body2 = new ExpressionReplacer { From = exp.Parameters[0], To = outerToInner.Body }.Visit(exp.Body);
    var lambda2 = Expression.Lambda<Func<TOuter, bool>>(body2, outerToInner.Parameters);
    return lambda2;
}

并且

public class ExpressionReplacer : ExpressionVisitor
{
    public Expression From;
    public Expression To;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == From)
        {
            return base.Visit(To);
        }

        return base.VisitParameter(node);
    }
}

你可以

// The initial "data"
string someValue = "Foo";
string someOtherValue = "Bar";
Expression<Func<MyInner, bool>> exp = t => t.Member1 == someValue && t.Member2 == someOtherValue;
Expression<Func<MyClass, MyInner>> outerToInner = u => u.Member;

// The "new" expression
Expression<Func<MyClass, bool>> result = Replace(exp, outerToInner);

ExpressionReplacer类用另一个表达式替换表达式的参数,而Replace方法使用ExpressionReplacer并重新构建一个新的表达式。


我已经实现了ExpressionReplacer并编写了一个测试,它运行良好,但当我在我们的项目中使用它时,它表现出奇怪的行为。它只会替换第一次出现的T为T1.T。 - Beatles1692
它将{x => ((x.RealEstateStatus == Advertised) AndAlso (((x.Longitude >= 51.37413024902344) AndAlso (x.Longitude <= 51.605701446533196)) AndAlso ((x.Latitude >= 35.65952786487723) AndAlso (x.Latitude <= 35.75250288098893))))}替换为{((x.RealEstateFile.RealEstateStatus == Advertised) AndAlso (((x.Longitude >= 51.37413024902344) AndAlso (x.Longitude <= 51.605701446533196)) AndAlso ((x.Latitude >= 35.65952786487723) AndAlso (x.Latitude <= 35.75250288098893))))} - Beatles1692
@Beatles1692 我已经进行了测试,在这里不起作用。你是否以某种方式组合表达式,或者像这样编写它 x => x.RealEstateStatus == value1 && x.Longitude >= value2 x.Longitude <= value3 && x.Latitude >= value4 && x.Latitude <= value5 - xanatos
@Beatles1692 因为我会说你正在编写表达式,可能使用了某些库,而各个“x”不是同一个“x”(Expression库不使用“string”名称进行参数匹配)。如果在调用我的方法之前尝试执行yourExpression.Compile(),它是否有效或抛出异常? - xanatos
我从未在此表达式上调用过 Compile,但是 Linq To Nhibernate 提供程序可以正确地将其翻译。 - Beatles1692
@Beatles1692,Linq to NHibernate 很可能使用属性的名称。NHibernate 的 QueryOver 会这样做。你是如何构建查询的呢?你是像我在三条消息之前给你的示例中那样,在单个表达式中构建它吗? - xanatos

3
你可以用几种方式来实现。
第一种,也是最简单的:使用Expression.Invoke
Expression<Func<T, bool>> exprT = t.Member1==someValue && t.Member2==someOtherValue
ParameterExpression p = Expression.Parameter(typeof(T1));
var expr = Expression.Invoke(expr, Expression.PropertyOrField(p, "Member"));
Expression<Func<T1, bool>> exprT1 = Expression.Lambda<Func<T1, bool>>(expr, p);

但在这种情况下,你得不到。
t1 => (t=>(t.Member1==someValue && t.Member2==someOtherValue))(t1.Member), 

替代

(t1=>t1.Member.Member1==someValue && t1.Member.Member2==someOtherValue);

如果要进行替换,您可以使用ExpressionVisitor类, 如下所示:

    class V : ExpressionVisitor
    {
        public ParameterExpression Parameter { get; private set; }
        Expression m;
        public V(Type parameterType, string member)
        {
            Parameter = Expression.Parameter(parameterType);
            this.m = Expression.PropertyOrField(Parameter, member);
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node.Type == m.Type)
            {
                return m;
            }
            return base.VisitParameter(node);
        }
    }

并使用它

var v = new V(typeof(T1), "Member");
var exprT1 = Expression.Lambda<Func<T1, bool>>(v.Visit(exprT.Body), v.Parameter);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接