按动态聚合分组

时间:2018-01-23 13:14:03

标签: linq expression-trees

在下面的代码中,我想将x.BaseSalary替换为名称存储在feildtoretrive中的任何其他属性:

var feildtoretrive  = "BaseSalary"
var groupcal = db.Employees.GroupBy(x=>x.Region)
                           .Select(group => new { 
                                    Key = group.Key, 
                                    Avg = group.Average(x => x.BaseSalary) 
                                                })
                           .ToList();

1 个答案:

答案 0 :(得分:3)

你需要

  • 创建一个访问该实例成员的MemberExpression
  • 编译一个lambda表达式,该表达式返回传递的实例参数
  • 的成员

(我假设Employees中的元素属于Employee

// the parameter expression for the lambda (your 'x')
var parameter = Expression.Parameter(typeof(Employee));
// the property access expression (your 'x.BaseSalary' or 'x.<feildtoretrive')
var propAccess = Expression.PropertyOrField(parameter, feildtoretrive);
// the build-together and compiled lambda
 var expression = (Expression<Func<Employee, int?>>)Expression.Lambda(propAccess, parameter);

您现在可以使用lambda进行x.Average来电:

new { Key = group.Key, Avg = group.Average(lambda) }

警告:这仅适用于int?类型的成员。我对如何做更多类型独立的方面缺乏一点经验,但是你可以用什么类型计算平均值?但是如果有intdouble个成员,则可能需要另一个强制转换表达式。

编辑:(将返回类型更改为int?)。根据{{​​3}}关于我的后续问题,你可以试试这个:

new { Key = group.Key, Avg = group.AsQueryable().Average(expression) }

EF6 应识别对AsQueryable()的调用,然后使用正确的Average方法(请注意,我使用expression作为参数而不是lambda 1}})。 EF Core和linq2Sql 不会使用它。