Aggiunta dinamica di un gruppo con un'espressione Lambda

expression-trees linq

Domanda

Ok, ammetto che non riesco ancora a "assimilare" le espressioni lambda e gli alberi di espressione LINQ; molto di quello che sto facendo è tagliare e incollare e vedere cosa funziona. Ho esaminato molta documentazione, ma ancora non ho ancora trovato il mio momento "aha".

Con quello detto ...

Sto tentando di aggiungere dinamicamente un'espressione GroupBy alla mia espressione Linq. Ho seguito la domanda qui: Ho bisogno di aiuto nella creazione di Linq.Expression a Enumerable.GroupBy

e ho cercato di implementare ciò che vedevo lì.

Innanzitutto, ho le classi di entità per il mio database e una tabella denominataObjCurLocViewNormalized

Ho un metodo che fa la chiamata iniziale,

public IQueryable<ObjCurLocViewNormalized> getLocations()
{
    IQueryable<ObjCurLocViewNormalized> res = (from loc in tms.ObjCurLocViewNormalized
                               select loc);
    return res;
}

quindi posso chiamare:

IQueryable<MetAmericanLinqDataModel.ObjCurLocViewNormalized> locations = american.getLocations();

Nessun problema finora.

Ora, voglio raggruppare per una colonna arbitraria, con una chiamata come questa:

var grouped = locations.addGroupBy(childLocationFieldName);

In questo momento, ho un metodo:

static public System.Linq.IQueryable<System.Linq.IGrouping<string, TResult>> addGroupBy<TResult>(this IQueryable<TResult> query, string columnName)
{

    var providerType = query.Provider.GetType();
    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var tableType = iqueryableT.GetGenericArguments()[0];
    var tableName = tableType.Name;

    var data = Expression.Parameter(iqueryableT, "query");
    var arg = Expression.Parameter(tableType, tableName);
    var nameProperty = Expression.PropertyOrField(arg, columnName);
    var lambda = Expression.Lambda<Func<TResult, string>>(nameProperty, arg);

    var expression = Expression.Call(typeof(Enumerable), 
                                    "GroupBy", 
                                    new Type[] { tableType, typeof(string) },
                                    data, 
                                    lambda);
    var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
    var result = query.GroupBy(predicate).AsQueryable();
    return result;
}

Tutto questo va bene, ma quando lo eseguo, ottengo l'errore:

System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Linq.IGrouping`2[System.String,MetAmericanLinqDataModel.ObjCurLocViewNormalized]]' cannot be used for return type 'System.String'

e l'errore viene da questa linea:

 var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg);

Sto copiando e adattando questo codice dal lavoro di successo che ho fatto aggiungendo in modo dinamico le clausole Where a un'espressione. Quindi sono una specie di pugnalata al buio qui.

Se qualcuno là fuori può aiutare a far luce su questo, ovviamente postare codice completo e fare tutto il mio pensiero per me sarebbe fantastico :), ma se potessi semplicemente spiegare perché questo è sbagliato, o come avvolgere la mia testa attorno a questi concetti, sarebbe fantastico. Se riesci a indirizzare la documentazione che può davvero aiutare a colmare il divario tra le basi delle espressioni lambda e costruire alberi di espressione dinamica, sarebbe fantastico. Ovviamente ci sono grandi buchi nella mia conoscenza, ma penso che questa informazione possa essere utile agli altri.

grazie a tutti per il vostro tempo e, naturalmente, se trovo la risposta altrove, la posterò qui.

Grazie ancora.

don

Risposta popolare

La soluzione dovrebbe essere piuttosto semplice:

public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(
    IQueryable<T> source, string column)
{
    PropertyInfo columnProperty = typeof(T).GetProperty(column);
    var sourceParm = Expression.Parameter(typeof(T), "x");
    var propertyReference = Expression.Property(sourceParm, columnProperty);
    var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);

    return source.GroupBy(groupBySelector);
}

Supponendo una classe di esempio come questa:

public class TestClass
{
    public string TestProperty { get; set; }
}

Lo invochi in questo modo:

var list = new List<TestClass>();
var queryable = list.AsQueryable();
DynamicGroupBy<TestClass, string>(queryable, "TestProperty");


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché