高效的LINQ to Entities查询

ome*_*fer 9 .net c# linq linq-to-entities entity-framework

我有一个实体集合Readings.每个Reading都链接到一个名为的实体Meter.(并且每个Meter都有多个读数).每个都Reading包含一个用于米标识(int)的字段和一个用于时间的字段.

这是一些简化的代码来演示它:

public class Reading
{
    int Id;
    int meterId;
    DateTime time;
}

public class Meter
{
    int id;
    ICollection<Readings> readings;    
}
Run Code Online (Sandbox Code Playgroud)

给定一个特定的时间段和meterids 列表,在每个仪表中获得该时间段内的第一个和最后一个读数的最有效方法是什么?

我可以遍历所有的仪表和每米来观察这段时间的第一次和最后一次阅读,但是如果有更有效的方法来实现这一点,我就会徘徊.

还有一个额外的问题:同样的问题,但有多个时间段来获取数据,而不仅仅是一个时期.

Dav*_*New 3

我不太确定您想要这些数据,但您可以将其投影为匿名类型:

var metersFirstAndLastReading = meters.Select(m => new 
    {
        Meter = m,
        FirstReading = m.readings.OrderBy(r => r.time).First(),
        LastReading = m.readings.OrderBy(r => r.time).Last()
    });
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样读取结果列表(此示例仅用作说明):

foreach(var currentReading in metersFirstAndLastReading)
{
    string printReadings = String.Format("Meter id {0}, First = {1}, Last = {2}", 
                               currentReading.Meter.id.ToString(),
                               currentReading.FirstReading.time.ToString(),
                               currentReading.LastReading.time.ToString());

    // Do something...
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是在 Meter 中创建动态返回第一个和最后一个读数的属性:

public class Meter
{
    public int id;
    public List<Reading> readings;

    public Reading FirstReading 
    {
        get
        {
            return readings.OrderBy(r => r.time).First();
        }
    }

    public Reading LastReading
    {
        get
        {
            return readings.OrderBy(r => r.time).Last();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我有点误解了这个问题。

以下是确定包含日期范围的仪表的第一个和最后一个读数的实现(假设是ID 的meterIdList一个,并且是指定的日期范围)ICollection<int>beginend

var metersFirstAndLastReading = meters
    .Where(m => meterIdList.Contains(m.id))
    .Select(m => new 
    {
        Meter = m,
        FirstReading = m.readings
                        .Where(r => r.time >= begin && r.time <= end)
                        .OrderBy(r => r.time)
                        .FirstOrDefault(),
        LastReading = m.readings
                        .Where(r => r.time >= begin && r.time <= end)
                        .OrderByDescending(r => r.time)
                        .FirstOrDefault()
    });
Run Code Online (Sandbox Code Playgroud)

您现在无法使用属性(因为您需要提供参数),因此方法可以作为替代方案正常工作:

public class Meter
{
    public int id;
    public List<Reading> readings;

    public Reading GetFirstReading(DateTime begin, DateTime end)
    {
        var filteredReadings = readings.Where(r => r.time >= begin && r.time <= end);

        if(!HasReadings(begin, end))
        {
            throw new ArgumentOutOfRangeException("No readings available during this period");
        }

        return filteredReadings.OrderBy(r => r.time).First();
    }

    public Reading GetLastReading(DateTime begin, DateTime end)
    {
        var filteredReadings = readings.Where(r => r.time >= begin && r.time <= end);

        if(!HasReadings(begin, end))
        {
            throw new ArgumentOutOfRangeException("No readings available during this period");
        }

        return filteredReadings.OrderBy(r => r.time).Last();
    }

    public bool HasReadings(DateTime begin, DateTime end)
    {
        return readings.Any(r => r.time >= begin && r.time <= end);
    }
}
Run Code Online (Sandbox Code Playgroud)