2016-04-18 14 views
1

Ich versuche, eine LinQ-Anfrage mit einer Gruppe von, wo ein Parameter ist parametrisierbar durch einen Ausdruck ((Expression<Func<CompanyModel,TKey>> myGroupingProperty) und der andere ist hart codiert. Aber selbst wenn mein Code kompiliert wird, bekomme ich einen Fehler, dass linq lambdas nicht unterstützt. Hätten Sie eine Idee, wie Sie diese Anfrage machen? HierLinq parametrisierbare Gruppe von mit 2 Eigenschaften

ist das Codebeispiel:

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

Was * genau * ist die Fehlermeldung? –

+0

Es wäre auch hilfreich, ein [mcve] anstatt nur ein Snippet bereitzustellen. –

+0

Das kann nicht funktionieren, Sie verwenden in einem Lambda, Sie versuchen, etwas wie zu tun: 'p => neu {p.Founded_Year, p2 => p2.PropertyToGroupBy}' mit geschachtelten Lambdas. Wahrscheinlich müssen Sie dazu Expression-Bäume verwenden oder den vollständigen Parameter p => new {p.Founded_Year, myGroupingProperty} 'als Parameter angeben und nicht nur einen Teil des Schlüssels (eine Klasse/Struktur erstellen, damit Sie nicht t muss mit einem anonymen Typ für den Schlüssel arbeiten). –

Antwort

2

Was Sie suchen nach ist machbar, aber nicht so, wie Sie versuchte, weil der übergebene Lambda-Ausdruck kann nicht direkt in einem anderen Lambda-Ausdruck verwendet werden.

Sie sollten zuerst beginnen, indem Sie eine generische Klasse für den Gruppierungsschlüssel erstellen. Es ist ähnlich dem System Tuple vorgesehen, hat aber parameterlosen Konstruktor und einfache Eigenschaft get/Setter zu den EF Projektionsregeln anzupassen:

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

Dann müssen Sie dieses

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

dynamisch Lambda-Ausdruck bauen In um das zu tun, werden Sie einige Expression Helfer benötigen:

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

und Sie können die Gruppierung an ac kapseln ustom Erweiterungsmethode:

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

Schließlich wird der wesentliche Teil der Methode wie folgt:

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