2016-04-18 3 views
1

Я пытаюсь выполнить запрос LinQ с группой, где параметр параметризуется выражением ((Expression<Func<CompanyModel,TKey>> myGroupingProperty), а другой жестко закодирован. Но даже если мой код компилируется, я получаю сообщение об ошибке, которое linq не поддерживает lambdas. Не могли бы вы представить, как это сделать?Linq параметризуемая группа по 2 свойствам

Вот пример кода:

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; 
} 
+0

Что * точно * этим сообщение об ошибке? –

+0

Было бы также полезно предоставить [mcve], а не просто фрагмент. –

+0

Это не может работать, вы используете в лямбда, вы пытаетесь сделать что-то вроде: 'p => new {p.Founded_Year, p2 => p2.PropertyToGroupBy}' с вложенными lambdas. Вам, вероятно, придется использовать деревья выражений для этого или предоставить полный параметр 'p => new {p.Founded_Year, myGroupingProperty} как параметр, а не только одну часть ключа (создание класса/структуры, t должен работать с анонимным типом для ключа). –

ответ

2

Что Вы ищете выполнимо, но не так, как вы пробовали, потому что прошел лямбда-выражение не может быть использован непосредственно внутри другого лямбда-выражения.

Начните сначала, создав универсальный класс для хранения ключа группировки. Это похоже на систему при условии Tuple, но имеет конструктор без параметров и простое свойство получить/сеттеры, чтобы соответствовать правилам проекционных EF:

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

Затем вам нужно построить динамически лямбда-выражения, как этот

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(); 
Смежные вопросы