簡體   English   中英

轉換成 LINQ 表達式樹

[英]Convert into LINQ expression tree

我有一個 LINQ 語句我想將它轉換成表達式樹

public class tblEmpLocation
{       
    public uint EmpLocationId { get; set; }            
    public uint? EmpId { get; set; }      
    public uint? LocationId { get; set; }      
    public DateTime EffectiveDt { get; set; } = DateTime.Now;      
}

我們有員工位置類,基本上有員工 ID 和位置 ID 的位置。

 public class temptable
{
   public uint Id{ get; set; }      
   public DateTime EffectiveDt { get; set; } 
}

我們有一個臨時類,它基本上包含作為示例類的 ID 和有效日期。 現在我們有類似的表,比如員工部門、員工工資等,所以我們想創建一個 linq 擴展,基本上將類作為輸入並獲得所需的結果

List<tblEmpLocation> tbls = new List<tblEmpLocation>();


var data=tbls.GroupBy(p => p.EmpId).Select(q => new temptable{ Id=q.Key, EffectiveDt=q.Max(r => r.EffectiveDt) });

感謝幫助

這是DistinctBy方法的通用實現,它返回組的最后一條記錄。

進行以下調用時的示意圖:

query = query.DistinctBy(e => e.EmpId, e => e.EffectiveDt);
// or with complex keys
query = query.DistinctBy(e => new { e.EmpId, e.Other }, e => new { e.EffectiveDt, e.SomeOther});

或全動態

query = query.DistinctBy("EmpId", "EffectiveDt");

函數生成以下查詢:

query = 
   from d in query.Select(d => d.EmpId).Distinct()
   from e in query
        .Where(e => e.EmpId == d)
        .OrderByDescending(e => e.EffectiveDt)
        .Take(1)
    select e;

或者使用復雜的鍵:

query = 
   from d in query.Select(d => new { d.EmpId, d.Other }).Distinct()
   from e in query
        .Where(e => e.EmpId == d.EmpId && e.Other == d.Other)
        .OrderByDescending(e => e.EffectiveDt)
        .ThenByDescending(e => e.SomeOther)
        .Take(1)
    select e;

和實現:

public static class QueryableExtensions
{
    public static IQueryable<T> DistinctBy<T>(
        this IQueryable<T> source,
        string distinctPropName,
        string maxPropName)
    {
        var entityParam = Expression.Parameter(typeof(T), "e");
        var distinctBy = Expression.Lambda(MakePropPath(entityParam, distinctPropName), entityParam);
        var maxBy = Expression.Lambda(MakePropPath(entityParam, maxPropName), entityParam);

        var queryExpression = Expression.Call(typeof(QueryableExtensions), nameof(QueryableExtensions.DistinctBy),
            new[] { typeof(T), distinctBy.Body.Type, maxBy.Body.Type },
            Expression.Constant(source),
            Expression.Quote(distinctBy),
            Expression.Quote(maxBy));

        var executionLambda = Expression.Lambda<Func<IQueryable<T>>>(queryExpression);
        return executionLambda.Compile()();
    }

    public static IQueryable<T> DistinctBy<T, TKey, TMax>(
        this IQueryable<T>        source, 
        Expression<Func<T, TKey>> distinctBy, 
        Expression<Func<T, TMax>> maxBy)
    {
        var distinctQuery = source.Select(distinctBy).Distinct();

        var distinctParam = Expression.Parameter(typeof(TKey), "d");
        var entityParam   = distinctBy.Parameters[0];

        var mapping = MapMembers(distinctBy.Body, distinctParam).ToList();

        var orderParam  = maxBy.Parameters[0];
        var oderMapping = CollectMembers(maxBy.Body).ToList();

        var whereExpr = mapping.Select(t => Expression.Equal(t.Item1, t.Item2))
            .Aggregate(Expression.AndAlso);
        var whereLambda = Expression.Lambda(whereExpr, entityParam);

        // d => query.Where(x => d.distinctBy == x.distinctBy).Take(1)
        Expression selectExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Where), new[] { typeof(T) },
            source.Expression,
            whereLambda);

        // prepare OrderByPart
        for (int i = 0; i < oderMapping.Count; i++)
        {
            var orderMethod = i == 0 ? nameof(Queryable.OrderByDescending) : nameof(Queryable.ThenByDescending);

            var orderItem = oderMapping[i];
            selectExpression = Expression.Call(typeof(Queryable), orderMethod, new[] { typeof(T), orderItem.Type },
                selectExpression, Expression.Lambda(orderItem, orderParam));
        }

        // Take(1)
        selectExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Take), new[] { typeof(T) },
            selectExpression,
            Expression.Constant(1));

        var selectManySelector =
            Expression.Lambda<Func<TKey, IEnumerable<T>>>(selectExpression, distinctParam);

        var selectManyQuery = Expression.Call(typeof(Queryable), nameof(Queryable.SelectMany),
            new[] { typeof(TKey), typeof(T) }, distinctQuery.Expression, selectManySelector);

        return source.Provider.CreateQuery<T>(selectManyQuery);
    }

    static Expression MakePropPath(Expression objExpression, string path)
    {
        return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
    }

    private static IEnumerable<Tuple<Expression, Expression>> MapMembers(Expression expr, Expression projectionPath)
    {
        switch (expr.NodeType)
        {
            case ExpressionType.New:
            {
                var ne = (NewExpression)expr;

                for (int i = 0; i < ne.Arguments.Count; i++)
                {
                    foreach (var e in MapMembers(ne.Arguments[i], Expression.MakeMemberAccess(projectionPath, ne.Members[i])))
                    {
                        yield return e;
                    }
                }
                break;
            }

            default:
                yield return Tuple.Create(projectionPath, expr);
                break;
        }
    }

    private static IEnumerable<Expression> CollectMembers(Expression expr)
    {
        switch (expr.NodeType)
        {
            case ExpressionType.New:
            {
                var ne = (NewExpression)expr;

                for (int i = 0; i < ne.Arguments.Count; i++)
                {
                    yield return ne.Arguments[i];
                }
                break;
            }

            default:
                yield return expr;
                break;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM