Linq可参数化组由2个属性组成

时间:2016-04-18 16:33:49

标签: c# linq group-by

我正在尝试使用组执行LinQ请求,其中参数可通过表达式((Expression<Func<CompanyModel,TKey>> myGroupingProperty)进行参数化,而另一个是硬编码的。但即使我的代码编译,我得到一个错误,linq不支持lambdas。你知道怎么做这个请求吗?

以下是代码示例:

public List<timelineResult> getTimelinebyCompany<TKey>(Expression<Func<CompanyModel,TKey>> myGroupingProperty, Filter item)
{
    int cntToBeSureThatTheQueryExecuteAtLeastOneTime = 0;
    List<timelineResult> toto = new List<timelineResult>();
    using (var db = new fintechDbContext())
    {         
        while (cntToBeSureThatTheQueryExecuteAtLeastOneTime == 0)
        {
             toto = (from p in db.companyDBSET                                    
                     select p).GroupBy(p=> new {p.Founded_Year, myGroupingProperty})
                             .Select(o => new timelineResult{ year = o.Key.Founded_Year, cluster = o.myGroupingProperty.ToString(), count = o.Count() })
                             .OrderBy(o => o.year).ToList();
            cntToBeSureThatTheQueryExecuteAtLeastOneTime++;
        }
    }

    return toto;
}

1 个答案:

答案 0 :(得分:2)

您正在寻找的是可行的,但不是您尝试的方式,因为传递的lambda表达式不能直接在另一个lambda表达式中使用。

首先应该创建一个通用类来保存分组键。它类似于系统提供的Tuple,但是具有无参数构造函数和简单的属性get / setter以符合EF投影规则:

public class GroupKey<K1, K2>
{
    public K1 Key1 { get; set; }
    public K2 Key2 { get; set; }
}

然后你需要像这样构建动态lambda表达式

Expression<Func<T, K1, K2>> keySelector = x => 
    new GroupKey<K1, K2> { Key1 = x.Prop1, Key2 = x.Prop2 };

为了做到这一点,你需要一些Expression助手:

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

您可以将分组部分封装在自定义扩展方法中:

public static class QueryableExtensions
{
    public static IQueryable<IGrouping<GroupKey<K1, K2>, T>> GroupByPair<T, K1, K2>(this IQueryable<T> source, Expression<Func<T, K1>> keySelector1, Expression<Func<T, K2>> keySelector2)
    {
        var parameter = keySelector1.Parameters[0];
        var key1 = keySelector1.Body;
        var key2 = keySelector2.Body.ReplaceParameter(keySelector2.Parameters[0], parameter);
        var keyType = typeof(GroupKey<K1, K2>);
        var keySelector = Expression.Lambda<Func<T, GroupKey<K1, K2>>>(
            Expression.MemberInit(
                Expression.New(keyType),
                Expression.Bind(keyType.GetProperty("Key1"), key1),
                Expression.Bind(keyType.GetProperty("Key2"), key2)),
            parameter);
        return source.GroupBy(keySelector);
    }
}

最后,您方法的基本部分如下:

toto = db.companyDBSET                                    
    .GroupByPair(p => p.Founded_Year, myGroupingProperty)
    .Select(g => new timelineResult
    { 
        year = g.Key.Key1,
        cluster = g.Key.Key2.ToString(),
        count = g.Count()
    })
    .OrderBy(o => o.year)
    .ToList();