【问题标题】:How to rebuild a lambda expression to start one level deeper?如何重建 lambda 表达式以开始更深一层?
【发布时间】:2017-07-16 15:39:35
【问题描述】:

我创建了一个通用方法,它接受一个标识分组键的成员访问表达式,就像传递给IQueryable<T>.GroupBy 一样。

private static IQueryable<ObjectWithRank<T>> IncludeBestRankPerGroup<T,TGroupKey>(this IQueryable<T> q, Expression<Func<T, TGroupKey>> keySelector)

class ObjectWithRank<T> {
    public T RankedObject { get; set; }
    public int Rank { get; set; }
}

IncludeBestRankPerGroup 方法是我的IncludeRank 方法的一种变体,它只采用IQueryable&lt;T&gt; 并通过将每个元素包装在ObjectWithRank&lt;T&gt; 中来对每个元素应用等级,返回一个IQueryable&lt;ObjectWithRank&lt;T&gt;&gt;。然后我想按keySelector 分组并选择每组中排名最高的元素。

这需要我将 lambda 表达式从形式 1 转换为 2,以便将其传递给 IQueryable&lt;ObjectWithRank&lt;T&gt;&gt;.GroupBy:

  1. (T x) =&gt; x.GroupingProperty
  2. (ObjectWithRank&lt;T&gt; x) =&gt; x.RankedObject.GroupingProperty

请注意,我不能只将keySelector 的根对象类型从T 更改为ObjectWithRank&lt;T&gt;,因为ObjectWithRank&lt;T&gt; 类未在调用IncludeBestRankPerGroup 的公共方法中公开。 API 的用户只提供了一个IQueryable&lt;T&gt;,然后收到了一个IQueryable&lt;T&gt;,其中包含每个组中排名最高的项目,因此他们永远不会看到ObjectWithRank&lt;T&gt; 在幕后使用。

我设法使用以下代码执行了转换,但它仅适用于简单的成员访问表达式。例如,它可以将x =&gt; x.GroupingKey 之类的表达式转换为x =&gt; x.RankedObject.GroupingKey,但它不适用于两级深度成员访问表达式,我必须将x =&gt; x.SubObject.GroupingKey 之类的内容转换为x =&gt; x.RankedObject.SubObject.GroupingKey。

private static Expression<Func<ObjectWithRank<T>, TGroupKey>> RebuildMemberAccessForRankedObject<T, TGroupBy>(Expression<Func<T, TGroupKey>> keySelector)
    {
        Expression<Func<ObjectWithRank<T>, T>> objectAccessExpression = x => x.RankedObject;
        return Expression.Lambda<Func<ObjectWithRank<T>, TGroupKey>>(
            Expression.Property(objectAccessExpression.Body, (keySelector.Body as MemberExpression).Member as PropertyInfo)
            , objectAccessExpression.Parameters
        );
    }

上面看起来像是一个 hack,我首先创建了一个访问 ObjectWithRank&lt;T&gt; 的 T RankedObject 属性的成员访问表达式,然后添加提供的 keySelector 成员访问表达式。我不确定是否有一种简单的方法可以让它工作。似乎 Expression.Property 一次只允许向下钻取一个属性,所以也许我需要某种循环来从顶部重建表达式,一次向下钻取一个属性。

这里有一个类似的问题,它确实有一个简单的解决方案,但在表达式的另一端更深一层,这不是我想要做的。 Alter Lambda Expression to go one level deeper

【问题讨论】:

    标签: c# lambda linq-to-entities expression


    【解决方案1】:

    我能够用递归lamba替换表达式的根,它在成员表达式中向下钻取直到它到达参数表达式,用最深层次的新根表达式替换参数表达式,然后展开调用堆栈将每个成员表达式的 Expression 替换为更新后的内部表达式,一直到顶部,然后使用更新后的表达式和为新根设置的参数表达式创建一个新的 lambda。

    private static Expression<Func<TInNew, TOut>> UpdateExpressionRoot<TOut, TInOld, TInNew>(Expression<Func<TInNew, TInOld>> newRoot, Expression<Func<TInOld, TOut>> memberAccess)
    {
        Func<MemberExpression, MemberExpression> updateDeepestExpression = null;
        updateDeepestExpression = e =>
        {
            if (e.Expression is MemberExpression)
            {
                var updatedChild = updateDeepestExpression((MemberExpression)e.Expression);
                return e.Update(updatedChild);
            }
            if (e.Expression is ParameterExpression)
                return e.Update(newRoot.Body);
            throw new ArgumentException("Member access expression must be composed of nested member access expressions only.", nameof(memberAccess));
        };
        return Expression.Lambda<Func<TInNew, TOut>>(updateDeepestExpression(memberAccess.Body as MemberExpression), newRoot.Parameters);
    }
    

    可以这样调用:

    class Car
    {
        Manufacturer Manufacturer { get; set; }
    }
    
    class Manufacturer
    {
        string ID { get; set; }
    }
    
    Expression<Func<Car, string>> groupKeySelector = x => x.Manufacturer.ID;
    Expression<Func<ObjectWithRank<Car>, Car>> rankedObjectSelector = x => x.RankedObject;
    
    var rankedGroupKeySelector = UpdateExpressionRoot(rankedObjectSelector, groupKeySelector);
    
    //rankedGroupKeySelector.ToString() == "x.RankedObject.Manufacturer.ID"
    //Essentially this replaced ParameterExpression {x} in x.Manufacturer.ID with MemberExpression {x.RankedObject}.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多