通用Expression.Call用于聚合函数

时间:2017-03-13 19:09:19

标签: c# .net

我正在尝试调整下面的代码来构建泛型函数,该函数返回聚合函数的表达式,例如sum,count average,min,max for data list

总和正在运作,但其他人则没有。我有附加信息:参数数量不正确异常。是的,显然是Expression.Call为其他人构建错误,但找不到任何doc如何为其他聚合函数构建正确的表达式。

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

用法:

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

1 个答案:

答案 0 :(得分:1)

要使其适用于Min,Max等,您需要进行一些更改(请参阅注释):

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

然而,Count是不同的,因为对于它来说,选择器没有任何意义(你不会调用Count(c => c.Value)),所以为此最好创建具有​​不同签名的单独方法(没有member)。