c#linq用于.GroupedBy()的Sum(Da​​taRow)的MethodCallExpression。选择()

时间:2018-05-31 11:40:39

标签: c# linq group-by sum expression

我编写了以下查询,该查询选择分组数据键列并对Amount列求和。它很完美。

    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"))
            });
        return querySelect;
    }

该查询使用以下类型进行分组和求和。

    private class GroupKey : IEquatable<GroupKey>
    {
        public string KeyS0 { get; set; }
        public string KeyS1 { get; set; }

        public bool Equals(GroupKey other)
        {
            if (ReferenceEquals(null, other))
                return false;
            if (ReferenceEquals(this, other))
                return true;
            return string.Equals(this.KeyS0, other.KeyS0) &&
                    string.Equals(this.KeyS1, other.KeyS1);
        }

        public override int GetHashCode()
        {
            int hash0 = this.KeyS0 == null ? 0 : this.KeyS0.GetHashCode();
            int hash1 = this.KeyS1 == null ? 0 : this.KeyS1.GetHashCode();
            return hash0 + 31 * hash1;
        }
    }

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

下一步,我想使用Linq表达式编写等效查询 我遇到了一个我不知道如何创建MethodCallExpression的问题:
g.Sum(row =&gt; row.Field(&#34; Amount&#34;))

我编写了以下代码。我在评论中标出了我被困的地方。

    private static void GetListOfGroupedRowsExpress()
    {
        //The MethodInfo for generic Field<T>(DataRow, String) can be retrieved by:
        MethodInfo methInfo = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });

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

        //Property to bind
        PropertyInfo propertyInfo = typeof(GroupSum).GetProperty("AggN0");

        //This returns properly: row.Field<double>("Amount")
        MethodCallExpression expCall = GetFieldCallExpression(expRow, methInfo, propertyInfo.PropertyType, "Amount");

        //This returns properly:  row => row.Field<double>("Amount")
        LambdaExpression expRowValues = Expression.Lambda<Func<DataRow, double>>(expCall, expRow);

        NewExpression expNewGroupKey = Expression.New(typeof(GroupSum));
        ParameterExpression expG = Expression.Parameter(typeof(GroupSum), "g");

        //This returns properly method info for: double Sum<T>()
        MethodInfo methodInfoSum = typeof(Queryable).GetMethods().First(m =>
            m.Name == "Sum"
            && m.ReturnType == typeof(double)
            && m.IsGenericMethod
            );
        //This returns properly method info for: double Sum<DataRow>()
        MethodInfo methodInfoSumDataRow = methodInfoSum.MakeGenericMethod(new Type[] { typeof(DataRow) });

        //And here I'm stuck. The code below compiles but at runtime it throws an error:
        //Expression of type 'TestLinq.TestLinqDataTable+GroupSum' cannot be used for parameter of type 'System.Linq.IQueryable`1[System.Data.DataRow]' of method 'Double Sum[DataRow](System.Linq.IQueryable`1[System.Data.DataRow], System.Linq.Expressions.Expression`1[System.Func`2[System.Data.DataRow,System.Double]])'
        MethodCallExpression expSumRows = Expression.Call(
            null,
            methodInfoSumDataRow,
            expG,
            expRowValues);
    }

    private static MethodCallExpression GetFieldCallExpression(ParameterExpression expRow, MethodInfo methodFieldGeneric,
                                                                Type type, string columnName)
    {
        List<Expression> list = new List<Expression>();
        list.Add(expRow);

        ConstantExpression expColumnName = Expression.Constant(columnName, typeof(string));
        list.Add(expColumnName);

        MethodInfo methodFieldTyped = methodFieldGeneric.MakeGenericMethod(type);

        MethodCallExpression expCall = Expression.Call(null, methodFieldTyped, list);
        return expCall;
    }

请问有人如何帮助我构建Sum()的调用表达式?

1 个答案:

答案 0 :(得分:1)

我对您的代码进行了一些更改:

private static Func<IGrouping<GroupKey, DataRow>, double> GetFunc()
{
    //row => row.Field<double>("Amount")
    //The MethodInfo for generic Field<T>(DataRow, String) can be retrieved by:
    MethodInfo methInfo = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });

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

    //Property to bind
    PropertyInfo propertyInfo = typeof(GroupSum).GetProperty(nameof(GroupSum.AggN0));

    //This returns properly: row.Field<double>("Amount")
    MethodCallExpression expCall = GetFieldCallExpression(expRow, methInfo, propertyInfo.PropertyType, "Amount");

    //This returns properly:  row => row.Field<double>("Amount")
    var expRowValues = Expression.Lambda(expCall, expRow);

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

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

    var sum = Expression.Lambda<Func<IGrouping<GroupKey, DataRow>, double>>(expSumRows, expQuerygroup);
    return sum.Compile();
}

private static MethodCallExpression GetFieldCallExpression(ParameterExpression expRow, MethodInfo methodFieldGeneric, Type type, string columnName)
{
    ConstantExpression expColumnName = Expression.Constant(columnName, typeof(string));

    MethodInfo methodFieldTyped = methodFieldGeneric.MakeGenericMethod(type);

    MethodCallExpression expCall = Expression.Call(null, methodFieldTyped, expRow, expColumnName);
    return expCall;
}

查找和处理泛型方法的Expression.Call有一个很棒的重载,你不需要一个数组/ List<>来调用Expression.Call,因为它有{{1}过载。

请注意,我已将您的代码更改为params ...我不认为您可以使用Enumerable执行所需操作...但您可以尝试将其更改回来。请注意,尽管您尝试在Queryable类型(仅用于发现AggN0类型的PropertyInfo propertyInfo)上使代码“通用”,但{{{ 1}}关键字出现在难以删除的地方(AggN0方法的返回类型)