如何构建Linq Expression中DataRow分组的Sum方法调用表达式?
解决LINQ Expression构建分组Sum调用的类型不匹配问题
我来帮你搞定这个头疼的类型错误!先拆解一下问题根源,再给你完整的正确实现方案。
错误原因分析
你的报错信息已经很明确了:你把GroupSum类型的表达式传给了需要IQueryable<DataRow>的Sum方法——这说明两个关键问题:
- 你误用了**
Queryable.Sum(针对IQueryable的扩展),但分组后的元素集合是IEnumerable<DataRow>,应该用Enumerable.Sum**(针对IEnumerable的扩展)。 - 你错误地传递了分组结果的封装类
GroupSum,而不是分组本身的元素集合(IGrouping<GroupKey, DataRow>实现了IEnumerable<DataRow>,直接传分组参数即可)。
先明确原始LINQ查询的结构
假设你原本可运行的LINQ代码是这样的:
var query = yourDataTable.AsEnumerable() .GroupBy(row => new GroupKey { // 这里是你的键列选择逻辑,比如取某个字段值 Category = row.Field<string>("Category") }) .Select(g => new GroupSum { Key = g.Key, TotalAmount = g.Sum(row => row.Field<double>("Amount")) });
正确的LINQ Expression实现步骤
下面是对应上述查询的完整Expression构建代码,我会标注关键注意点:
首先先定义你的自定义类型(假设是这样):
public class GroupKey { public string Category { get; set; } // 示例键属性,根据你的实际定义调整 } public class GroupSum { public GroupKey Key { get; set; } public double TotalAmount { get; set; } }
然后是Expression构建代码:
// 1. 定义DataRow类型的参数,用于后续的键选择器和Sum选择器 ParameterExpression rowParam = Expression.Parameter(typeof(DataRow), "row"); // 2. 构建GroupBy的键选择器:row => new GroupKey { Category = row.Field<string>("Category") } MemberInitExpression keySelector = Expression.MemberInit( Expression.New(typeof(GroupKey)), Expression.Bind( typeof(GroupKey).GetProperty(nameof(GroupKey.Category)), Expression.Call( typeof(DataRowExtensions), "Field", new[] { typeof(string) }, rowParam, Expression.Constant("Category") // 替换为你的实际键列名 ) ) ); // 3. 定义分组参数:类型是IGrouping<GroupKey, DataRow>,对应原始查询中的"g" ParameterExpression groupParam = Expression.Parameter(typeof(IGrouping<GroupKey, DataRow>), "g"); // 4. 构建Sum的选择器:row => row.Field<double>("Amount") Expression sumSelectorLambda = Expression.Lambda<Func<DataRow, double>>( Expression.Call( typeof(DataRowExtensions), "Field", new[] { typeof(double) }, rowParam, Expression.Constant("Amount") ), rowParam ); // 5. 关键:构建正确的Sum方法调用 // 这里必须用Enumerable.Sum,因为分组是IEnumerable<DataRow>,不是IQueryable MethodCallExpression sumCall = Expression.Call( typeof(Enumerable), "Sum", new[] { typeof(DataRow) }, // 泛型参数:元素类型DataRow groupParam, // 第一个参数:分组的IEnumerable<DataRow>集合(IGrouping本身实现了IEnumerable) sumSelectorLambda // 第二个参数:Sum的选择器Lambda ); // 6. 构建Select的结果:g => new GroupSum { Key = g.Key, TotalAmount = sumCall } MemberInitExpression selectResult = Expression.MemberInit( Expression.New(typeof(GroupSum)), Expression.Bind( typeof(GroupSum).GetProperty(nameof(GroupSum.Key)), Expression.Property(groupParam, nameof(IGrouping<GroupKey, DataRow>.Key)) ), Expression.Bind( typeof(GroupSum).GetProperty(nameof(GroupSum.TotalAmount)), sumCall ) ); // 7. 组装完整的查询表达式 var dataTableParam = Expression.Parameter(typeof(DataTable), "dt"); var finalQuery = Expression.Lambda<Func<DataTable, IEnumerable<GroupSum>>>( Expression.Call( typeof(Enumerable), "Select", new[] { typeof(IGrouping<GroupKey, DataRow>), typeof(GroupSum) }, // 先调用GroupBy Expression.Call( typeof(Enumerable), "GroupBy", new[] { typeof(DataRow), typeof(GroupKey) }, Expression.Call(typeof(DataTableExtensions), "AsEnumerable", Type.EmptyTypes, dataTableParam), Expression.Lambda<Func<DataRow, GroupKey>>(keySelector, rowParam) ), // 再调用Select Expression.Lambda<Func<IGrouping<GroupKey, DataRow>, GroupSum>>(selectResult, groupParam) ), dataTableParam ); // 编译并执行 var queryFunc = finalQuery.Compile(); var result = queryFunc(yourDataTable);
核心注意事项
- 选对Sum方法:绝对不要用
Queryable.Sum,分组后的元素是内存中的IEnumerable<DataRow>,必须用Enumerable.Sum。 - 分组参数的类型:一定要用
IGrouping<GroupKey, DataRow>作为分组参数的类型,它本身就是IEnumerable<DataRow>,直接传给Sum即可。 - Lambda参数一致性:构建Sum选择器时用的
rowParam,要和之前定义的参数完全一致,避免类型不匹配。
内容的提问来源于stack exchange,提问作者Sylwester Santorowski
相关产品推荐
相关产品推荐

