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

问题描述 投票:1回答:1

我编写了以下查询,该查询选择分组数据键列并对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 => row.Field(“Amount”))

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

    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()的调用表达式?

c# linq group-by sum expression
1个回答
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,因为它有一个params重载。

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

© www.soinside.com 2019 - 2024. All rights reserved.