Search code examples
c#linqgroup-bymaxexpression-trees

c# linq MethodCallExpression for Max(DataRow) used for .GroupedBy().Select()


The question hereby refers to my previous question here The Sum() expression advised by @xanatos worked perfectly. I tried also Max() expression for the field of the type double and faced no issue.
As the next step I decided to add the DateTime field and calculate Max() for it.
I modified the GroupSum class as follows:

private class GroupSum : GroupKey
{
    public Double AggN0 { get; set; }
    public DateTime AggD0 { get; set; }
}

And programmed the function:

private static Func<IGrouping<GroupKey, DataRow>, DateTime> GetFuncMaxDateTime()
{
    MethodInfo methInfo = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });

    ParameterExpression expRow = Expression.Parameter(typeof(DataRow), "row");  //Parametr: (row =>....)

    PropertyInfo propertyInfo = typeof(GroupSum).GetProperty("AggD0");

    MethodCallExpression expCall = GetFieldCallExpression(expRow, methInfo, propertyInfo.PropertyType, "DocumentDate");

    var expRowValues = Expression.Lambda(expCall, expRow);

    ParameterExpression expQuerygroup = Expression.Parameter(typeof(IGrouping<GroupKey, DataRow>), "g");

    //HERE it throws an error: No generic method 'Max' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. 
    MethodCallExpression expMaxRows = Expression.Call(typeof(Enumerable), "Max", new[] { expRow.Type }, expQuerygroup, expRowValues);

    var max = Expression.Lambda<Func<IGrouping<GroupKey, DataRow>, DateTime>>(expMaxRows, expQuerygroup);
    return max.Compile();
}

The code compiles but it throws an error at runtime. I commented the instruction and specified the error message. It is strange because the only difference is
double Sum(double)
vs
DateTime Max(DateTime)

No need to add that hardcoded version works ok.

private static IEnumerable<GroupSum> GetListOfGroupedRows(IEnumerable<IGrouping<GroupKey, DataRow>> queryGroup)
{
    IEnumerable<GroupSum> querySelect = queryGroup
        .Select(g => new GroupSum
        {
            KeyS0 = g.Key.KeyS0,
            KeyS1 = g.Key.KeyS1,
            AggN0 = g.Sum(row => row.Field<double>("Amount")),
            AggD0 = g.Max(row => row.Field<DateTime>("DocumentNumber"))
        });
    return querySelect;
}

Solution

  • Change this line adding expRowValues.ReturnType:

    MethodCallExpression expMaxRows = Expression.Call(
        typeof(Enumerable), 
        nameof(Enumerable.Max), 
        new[] { expRow.Type, expRowValues.ReturnType }, 
        expQuerygroup, 
        expRowValues);
    

    because you want this overload:

    public static TResult Max<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
    

    so two generic type parameters!