2013-06-05 66 views
0

以下是我正在尝试执行的操作: 我的ASP.NET Web API从客户端接受图表“id”,“startTimestamp”和“endTimestamp”。我需要Web API仅选择由startTimestamp和endTimestamp定义的时间戳范围内的“MeasureData”(请参阅​​附加的EF5概念模型图)。我的Web API将JSON AmSerialChart对象图返回给客户端。然后解析JSON对象图并使用amCharts Javascript API构建图表。该图表仅显示startTimestamp和endTimestamp之间的数据。在对象图中过滤集合

我无法弄清楚如何在我的控制器中编写一个LINQ表达式,它将过滤MeasureData并返回一个带有过滤的MeasureData的AmSerialChart对象。

这是一个失败的尝试:

AmSerialChart amserialchart = 
(AmSerialChart)db.AmSerialCharts 
.Find(id) 
.Graphs 
.Select(g => g.Measure) 
.SelectMany(m => m.MeasureData) 
.Where(md => md.Timestamp >= startDate && md.Timestamp <= endDate); 

“db.AmSerialCharts.Find(ID)”找到合适的图表实体,但我不知道如何通过对象图的其余部分导航进入MeasureData过滤器。

+0

Graph表的要点是什么? – Justin

+0

@Justin - 一个Graph是一个图表对象。每个AmSerialChart可以有多个图。最后,我会为Graph对象添加属性,例如线条颜色,线条粗细等。每个图形只与一个Measure关联。但是,一个度量可以由多个图形绘制。 – MikeyWazz

回答

1

有时单线LINQ不是解决问题的最佳解决方案。即使可能,可读性通常也会被忽略。有时最好手动完成这项工作。

您的请求的问题在于,您希望AmSerialChart对象的修剪完整对象层次结构与您的时间范围匹配。问题是基本的Select不会那样做。它会想要生成一个新的列表或新的对象。

我觉得你最好的解决办法是“复制”的对象层次结构,而不是试图LINQ它:

var fullChart = charts.Where(c => c.Id == id).Single(); 
var result = new AmSerialChart() 
{ 
    Id = fullChart.Id, 
    Name = fullChart.Name, 
    Description = fullChart.Description, 
    FirstTimestamp = fullChart.FirstTimestamp, 
    LastTimestamp = fullChart.LastTimestamp, 
    Graphs = new List<Graph>() 
}; 

foreach (var graph in fullChart.Graphs) 
{ 
    var measure = graph.Measure; 

    var newMeasure = new Measure() 
    { 
    Id = measure.Id, 
    // Copy the rest of data 

    MeasureData = new List<MeasureData>() 
    }; 

    foreach (var measureData in measure.MeasureData) 
    { 
    if (measureData.Timestamp >= startDate && measureData.Timestamp <= endDate) 
    { 
     var newMeasureData = new MeasureData() 
     { 
     Id = measureData.Id, 
     // Copy the rest of data 
     }; 
     newMeasure.MeasureData.Add(newMeasureData); 
    } 
    } 

    if (newMeasure.MeasureData.Count() > 0) 
    { 
    var newGraph = new Graph() 
    { 
     Id = graph.Id, 
     // Copy the rest of data 

     Measure = newMeasure 
    }; 
    result.Graphs.Add(newGraph); 
    } 
} 
+0

感谢您提供有关跳过Measure对象的提示。但是,您的答案将返回MeasureData的IEnumerable而不是AmSerialChart。测试你的答案也让我意识到,我不认为我想要一个SelectMany运算符,因为它将所有度量中的所有MeasureDatas展平。 – MikeyWazz

+0

您是否想要一个具有'Graph'对象的'AmSerialChart'对象和'MeasureData'修剪成只包含您的时间范围内的数据?如果由'id'标识的'AmSerialChart'没有这样的度量数据,你还想要'AmSerialChart'吗? –

+0

可能很难给你一个过滤到你请求的数据的对象。原因是您的源对象是具有完整数据的“完整”对象,您必须将所有对象复制到层次结构中,以便不修改原始“完整数据”版本。您不仅能够“选择”修剪过的版本,而且没有可能的负面影响。 –

1

过滤MeasureData并返回AmSerialChart对象

您更好地利用Any()

var measureData = charts.Where(c => c.Id == id 
    && c.Graphs.Any(g => g.Measure.MeasureData 
    .Any(md => md.Timestamp >= startDate && md.Timestamp <= endDate))) 
    .FirstOrDefault(); 

SingleOrDefault()如果不超过1场比赛预计。

+0

尽管此答案正在返回一个AmSerialChart对象(+1),但它不会过滤掉落在startDate-endDate范围之外的MeasureData实体。 – MikeyWazz

+0

如果您使用'All()'而不是'Any()'(两次),您将选择一个'AmSerialChart',其_all_图表_only_在指定的时间间隔内有MeasureData。通常这是非常(非常)严格的条件,但它对您的数据可能是正确的。 –

+0

当我使用'Any()'(两次)时,只要其中至少一个时间戳在该范围内,就会返回所有MeasureData。当我使用'All()'(两次)时,如果其中至少有一个时间戳超出范围,则不会返回MeasureData。这是有道理的,但我想要做的是返回其时间戳在范围内的MeasureData,并排除其时间戳超出范围的MeasureData。在我看来,我应该对MeasureData集合应用'Where()'运算符,但我不确定如何在LINQ语句中执行此操作。 – MikeyWazz