2017-03-13 6 views
1

Ich versuche, den Code unten anzupassen generische Funktion zu bauen, den Ausdruck für Aggregatfunktionen wie Summe zurückgibt, zählt Durchschnitt, min, max für die Liste der DatenGenerisches Expression.Call für Aggregatfunktionen

Sum arbeitet aber andere sind nicht. Ich habe zusätzliche Informationen: Falsche Anzahl der Argumente Ausnahme. Ja, es ist klar, dass Expression.Call für andere falsch erstellt wurde, aber kein doc finden kann, um den richtigen Ausdruck für andere Aggregatfunktionen zu erstellen.

public Expression AggregateFunc(IQueryable source, string member, string aggFunc) 
{ 
    // Properties 
    PropertyInfo property = source.ElementType.GetProperty(member); 
    FieldInfo field = source.ElementType.GetField(member); 
    ParameterExpression parameter = Expression.Parameter(source.ElementType, "f"); 
    Expression selector = Expression.Lambda(Expression.MakeMemberAccess(parameter, (MemberInfo)property ?? field), parameter);    
    // Method 
    var l = typeof(Queryable).GetMethods().Where(m => m.Name == aggFunc).ToList(); 
    MethodInfo method = typeof(Queryable).GetMethods().First(m => m.Name == aggFunc); 
    return Expression.Call(
    null, 
    method.MakeGenericMethod(new[] { source.ElementType }), 
    new[] { source.Expression, Expression.Quote(selector) }); 
} 

Verbrauch:

var list = new List<Int32FormFieldData>() 
{ 
    new FormFieldData { Path = "1", Value = 1 }, 
    new FormFieldData { Path = "2", Value = 2 }, 
    new FormFieldData { Path = "3", Value = 3 } 
};` 
AggregateFunc(list.AsQueryable(), "Value", "Count"); 

Antwort

1

Um es mit Min, Max usw. funktioniert, müssen Sie einige Änderungen vornehmen (siehe Kommentare):

public static Expression AggregateFunc(IQueryable source, string member, string aggFunc) { 
    PropertyInfo property = source.ElementType.GetProperty(member); 
    FieldInfo field = source.ElementType.GetField(member);   
    ParameterExpression parameter = Expression.Parameter(source.ElementType, "f"); 
    Expression selector = Expression.Lambda(Expression.MakeMemberAccess(parameter, (MemberInfo) property ?? field), parameter); 
    // Method 
    // find correct method with two parameters: IQueryable and selector    
    MethodInfo method = typeof(Queryable).GetMethods().Where(c => c.GetParameters().Length == 2).First(m => m.Name == aggFunc); 
    // some aggregates have two generic type arguments (such as min, max, average) 
    // others like Sum have just one 
    var genArgs = new List<Type>(); 
    genArgs.Add(source.ElementType); 
    if (method.GetGenericArguments().Length > 1) { 
     genArgs.Add(property?.PropertyType ?? field.FieldType); 
    } 
    return Expression.Call(
     null, 
     method.MakeGenericMethod(genArgs.ToArray()), 
     new[] {source.Expression, Expression.Quote(selector)}); 
} 

jedoch Graf ist anders, weil dafür macht Selektor keinen Sinn (Sie rufen nicht Count(c => c.Value)), so dass es besser ist, separate Methode mit unterschiedlicher Signatur zu erstellen (ohne member).