c# linq MethodCallExpression for Sum( DataRow) used for .GroupedBy().Select()
我对以下查询进行了编程,该查询选择了分组的数据关键字列并对Amount列求和。效果很好。
1 2 3 4 5 6 7 8 9 10 11 | 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; } |
查询使用以下类型进行分组和求和。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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 "))
我编写了以下代码。我在卡住的评论中标记了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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; } |
请注意,我已将您的代码更改为