【问题标题】:Convert an Expression<Func<T,bool>> to an Expression<Func<T1,bool>> so that T is a member of T1将 Expression<Func<T,bool>> 转换为 Expression<Func<T1,bool>> 以便 T 是 T1 的成员
【发布时间】:2015-04-27 10:44:12
【问题描述】:

我们有一个T1 类型的实体,它有一个T 类型的成员。 像这样:

public class T1
{
    public T Member{get;set;}
}

用户可以使用我们的 UI 为我们提供一个过滤 T 的过滤器,我们已将其转换为一个函数表达式,该表达式获取一个 T 并返回 bool (Expression&lt;Func&lt;T,bool&gt;&gt;)

我想知道是否可以将其转换为获取 T1 并返回 bool 的函数的表达式。

其实我想转换这个:

(t=>t.Member1==someValue && t.Member2==someOtherValue);

到这里:

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

【问题讨论】:

  • 我假设编译器没有静态可用的这些信息。我知道 T->bool 表达式是动态提供的,但是 T1 和 Member 是静态可用的吗?您还需要将其作为表达式还是可以将其转换为委托?
  • 你可以试试Invoke
  • 因为我必须将它传递给 Linq 提供程序并且我必须使用 IQueryables 我认为我必须将它们作为表达式?
  • @Grundy 有人说答案在于 Invoke 但我仍然看不出我应该如何使用它:)
  • 呵呵,我喜欢发布我对其他问题的回答,我想这也可以帮助你:P stackoverflow.com/questions/29448432/…

标签: c# lambda expression-trees


【解决方案1】:

你可以通过几种方式做到这一点。

第一个也是最简单的:使用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 classlike

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

【讨论】:

    【解决方案2】:

    给定

    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 。
    • 它将{x =&gt; ((x.RealEstateStatus == Advertised) AndAlso (((x.Longitude &gt;= 51.37413024902344) AndAlso (x.Longitude &lt;= 51.605701446533196)) AndAlso ((x.Latitude &gt;= 35.65952786487723) AndAlso (x.Latitude &lt;= 35.75250288098893))))} 替换为{((x.RealEstateFile.RealEstateStatus == Advertised) AndAlso (((x.Longitude &gt;= 51.37413024902344) AndAlso (x.Longitude &lt;= 51.605701446533196)) AndAlso ((x.Latitude &gt;= 35.65952786487723) AndAlso (x.Latitude &lt;= 35.75250288098893))))}
    • @Beatles1692 我做了一个测试,但这里没有。你是以任何方式编写表达式还是像x =&gt; x.RealEstateStatus == value1 &amp;&amp; x.Longitude &gt;= value2 x.Longitude &lt;= value3 &amp;&amp; x.Latitude &gt;= value4 &amp;&amp; x.Latitude &lt;= value5 这样写?
    • @Beatles1692 因为我会说您正在编写表达式,可能是通过使用某个库,并且各种“x”不是同一个“x”(表达式库不使用用于参数匹配的“字符串”名称)。如果您在调用我的方法之前尝试执行yourExpression.Compile(),它会起作用还是会引发异常?
    • 我从来没有在这个表达式上调用过Compile,但是Linq To Nhibernate提供者正确地翻译了它。
    猜你喜欢
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多