Reputation: 109
Here is what I am trying to do: My ASP.NET Web API accepts a chart "id", a "startTimestamp", and an "endTimestamp" from the client. I need the Web API to select only the "MeasureData" (see the attached EF5 conceptual model diagram) that is inside of the timestamp range defined by startTimestamp and endTimestamp. My Web API returns a JSON AmSerialChart object graph to the client. I then parse the JSON object graph and build a chart using the amCharts Javascript API. The chart only shows data between the startTimestamp and endTimestamp.
I can't figure out how to write a LINQ expression in my controller that will filter the MeasureData and return an AmSerialChart object with filtered MeasureData.
Here is one failed attempt:
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)" finds the appropriate chart entity, but I'm not sure how to navigate through the rest of the object graph to get to the MeasureData filter.
Upvotes: 0
Views: 309
Reputation: 36083
Sometimes a single-line LINQ is not the best solution for a problem. Even if it's possible, readability will often go out the window. Sometimes it's just best to manually do the work.
The problem with your request is that you want an AmSerialChart
object with a full object hierarchy pruned to match your timeframe. The issue is that a basic Select
won't do that. It will want to generate a new list or new objects.
I think your best solution is to "copy" your object hierarchy rather than trying to LINQ it:
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);
}
}
Upvotes: 1
Reputation: 109205
filter the MeasureData and return an AmSerialChart object
You better use 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();
or SingleOrDefault()
if not more than 1 match is expected.
Upvotes: 1