在Linq中动态选择列和聚合函数

时间:2015-08-05 22:32:56

标签: c# linq linq-expressions

我在Linq中有一个查询需要根据用户在表单中选择的某些值进行动态调整。我的查询如下:

(from data in DbContext.CoacheeData
 group data by data.UserId into g
 select new LeaderboardEntry
 {
     Name = g.FirstOrDefault().UserName,
     Value = g.Sum(data => data.Steps),
 }); 

我希望此查询的两个方面是动态的:

  • 用于计算价值的汇总函数(可以是总和,平均值或最大值)
  • 用于计算值的列(见下文)

我一直在尝试 - 并且失败 - 为此使用Linq表达式,但我不断收到运行时错误。我在Linq很新,所以我可能做了些蠢事。

以下是CoacheeData类的摘录。如果有帮助,所有属性都是数字。除了步骤之外,所有浮动都是均匀的。

public class CoacheeData
{
    [Key]
    public long Id { get; set; }

    [Required]
    [ForeignKey("User")]
    public string UserId { get; set; }

    public virtual ApplicationUser User { get; set; }

    public DateTime Date { get; set; }

    //Each of these properties should be selectable by the query
    public int Steps { get; set; }
    public float Distance { get; set; }
    public float CaloriesBurned { get; set; }
    //...etc
}

编辑:我被问到我尝试了什么,下面是我认为最相关的事情:

Func<CoacheeData, int> testLambda = (x) => x.Steps;

//Used inside the query like this:
Value = g.Sum(testLambda),

这会引发异常(&#34;内部.NET Framework数据提供程序错误1025。&#34;),我认为这是正常的,因为您无法在Linq to SQL中直接使用lambda 。如果我错了,请纠正我。所以我尝试了一个表达式:

Expression<Func<CoacheeData, int>> varSelectExpression = (x) => x.Steps;

//And in the query:
Value = g.Sum(varSelectExpression.Compile()),

抛出相同的错误1025,这是按预期工作的,因为Compile()调用再次将表达式更改为lambda,在我的理解中。但是我不能省略Compile()调用,因为Linq代码没有编译。不知道如何解决这个问题。

我尝试了类似的事情,我怀疑我的错误(和解决方案)将具有可比性。如果需要,我稍后会添加更多信息。

3 个答案:

答案 0 :(得分:0)

您可以使用DynamicLINQ库。这些允许您使用字符串构造查询。以下是一些链接(其中一些可能是彼此的扩展 - 每个链接看起来都不太多。)

https://www.nuget.org/packages/System.Linq.Dynamic/

http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library

https://dynamiclinq.codeplex.com/

答案 1 :(得分:0)

类型安全的解决方案是动态构建result.AsyncState表达式。我的组件AdaptiveLINQ就是为此而设计的。

它在LINQ查询中引入了 cube 的概念。

只需定义一个具有1个动态维度和1个动态度量的多维数据集。

GroupBy

并使用class MyCubeItem { public string Name { get; set; } public double Value { get; set; } } class MyCube : ICubeDefinition<CoacheeData, MyCubeItem> { public string DimensionName { get; set; } public string AggregateName { get; set; } public string AggregateOperator { get; set; } public Expression<Func<CoacheeData, string> Name { get { switch (DimensionName) { case "UserId": return x => x.UserId; ... } } } public Expression<Func<CoacheeData, string> Value { get { switch (AggregateName) { case "Steps": switch (AggregateOperator) { case "Sum": return items => items.Sum(x => x.Steps); ... } } } } } LINQ扩展方法:

QueryByCube

答案 2 :(得分:0)

你几乎拥有它。为此,您只需在传入表达式时使用扩展方法。 EF的linq to sql不支持将表达式传递给表达式。

请注意,如果其中一种数值数据类型,则以下方法效果最佳。我正在使用float;

var baseQ  = (from data in DbContext.CoacheeData
              group data by data.UserId into g
              select g);
Expression<Func<CoacheeData,float> expression;
switch(propName){
    case "Distance": 
         expression = x=> x.Distance; break;
    case "CaloriesBurned":
         expression = x=>x.CaloriesBurned; break;
}

switch(aggregateFn) {

     case AggregateFn.Sum: 
          return baseQ.Sum(expression);
     case AggregateFn.Max: 
          return baseQ.Max(expression);   

     //...      
}

AggregateFn是此示例中的枚举。