Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 高效的LINQ到实体查询_C#_.net_Linq_Entity Framework_Linq To Entities - Fatal编程技术网

C# 高效的LINQ到实体查询

C# 高效的LINQ到实体查询,c#,.net,linq,entity-framework,linq-to-entities,C#,.net,Linq,Entity Framework,Linq To Entities,我有一个读数的实体集合。 每个读数都链接到一个名为仪表的实体。 (并且每个仪表都有多个读数)。 每个读数都包含一个仪表id(int)字段和一个时间字段 下面是一些简化的代码来演示: public class Reading { int Id; int meterId; DateTime time; } public class Meter { int id; ICollection<Readings> readings; } 公共课

我有一个
读数的实体集合
。 每个
读数
都链接到一个名为
仪表
的实体。 (并且每个
仪表
都有多个读数)。 每个
读数
都包含一个仪表id(int)字段和一个时间字段

下面是一些简化的代码来演示:

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

public class Meter
{
    int id;
    ICollection<Readings> readings;    
}
公共课阅读
{
int-Id;
内特梅特里德;
日期时间;
}
公共类仪表
{
int-id;
i收集读数;
}
给定特定的时间段和
meterid
s列表, 每米最有效的方式是什么 那个时期的第一次和最后一次阅读

我能够遍历所有米,并为每米遍历obatin 这一时期的第一次和最后一次阅读, 但我想知道是否有更有效的方法来实现这一点

还有一个奖金问题:同一个问题,但需要多个时段来获取数据,
而不仅仅是一个句点。

我不确定您希望如何使用此数据,但您可以将其投影为匿名类型:

var metersFirstAndLastReading = meters.Select(m => new 
    {
        Meter = m,
        FirstReading = m.readings.OrderBy(r => r.time).First(),
        LastReading = m.readings.OrderBy(r => r.time).Last()
    });
然后,您可以像这样阅读结果列表(此示例仅用于说明):

另一个选项是在仪表中创建属性,动态返回第一个和最后一个读数:

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();
        }
    }
}
您现在将无法使用属性(因为您需要提供参数),因此方法可以作为替代方法正常工作:

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);
    }
}
公共类仪表
{
公共int id;
公开阅读书目;
公共读取GetFirstReading(日期时间开始,日期时间结束)
{
var filteredrardings=reads.Where(r=>r.time>=begin&&r.time r.time).First();
}
公共读取GetLastReading(日期时间开始,日期时间结束)
{
var filteredrardings=读数。其中(r=>r.time>=开始和&r.time r.time).Last();
}
公共布尔值(日期时间开始,日期时间结束)
{

return reads.Any(r=>r.time>=begin&&r.time创建一个新类作为名为
Result
的返回类型,如下所示

public class Result
{
    public int MeterId;
    public Readings Start;
    public Readings Last;
}
我模拟了您的情况,制作了一个仪表列表并填充了一些数据,但是您的查询应该基本相同

var reads = Meters.Where(x => x.readings != null)
                  .Select(x => new Result
                          {
                              MeterId = x.id,
                              Start = x.readings.Select(readings => readings).OrderBy(readings=>readings.time).FirstOrDefault(),
                              Last = x.readings.Select(readings=>readings).OrderByDescending(readings=>readings.time).FirstOrDefault()
                          });
public IEnumerable GetFirstAndLastInPeriod
(IEnumerable reads,日期时间开始,日期时间结束)
{
返回
从阅读中学习
让span=读数。其中(item=>item.time>=begin&&item.time item.time)
||reading.time==span.Min(item=>item.time)
选择阅读;
}
meters.Where(mt=>desiredMeters.Contains(mt))。选择(mt=>
新的{
西德山,
First=mt.Readings.Where().OrderBy(rd=>rd.Time).FirstOrDefault(),
Last=mt.Readings.Where().OrderBy(rd=>rd.Time).LastOrDefault()
});

如果每米有大量的读数,这将不能很好地执行,并且你应该把读数看作是排序列表类。

< P>我的解决方案将精确地返回你想要的<强>(在给定时间段内包含所有读数的列表)< /强>

public IList GetFirstandLastReaders(列表meterList、DateTime start、DateTime end)
{       
IList fAndlReadingsList=新列表();
meterList.ForEach(x=>x.Reads.ForEach(y=>
{
var readingList=新列表();
if(y.time>=startTime&&y.time reading.time).First(),readingList.OrderBy(reading=>reading.time.Last());
}
}));
返回FandReadingList;
}

我有一个非常相似的数据模型,其中该代码用于获取最旧的读数,我只是将其更改为包含最新的读数

我使用查询语法执行以下操作:

var query = from reading in db.Readings
            group reading by reading.meterId
            into readingsPerMeter
            let oldestReadingPerMeter = readingsPerMeter.Min(g => g.time)
            let newestReadingPerMeter = readingsPerMeter.Max(g => g.time)
            from reading in readingsPerMeter
            where reading.time == oldestReadingPerMeter || reading.time == newestReadingPerMeter 
            select reading; //returns IQueryable<Reading> 
query.Where(r=>r.time > someTime1 && r.time < someTime2)
var query=从以db.reads为单位的读取
按reading.meterId分组阅读
读数渗透计
设oldestReadingPerMeter=readingpermeter.Min(g=>g.time)
设newestReadingPerMeter=readingpermeter.Max(g=>g.time)
从读数渗透计中读取
其中reading.time==旧的读取Permeter | | reading.time==新的读取Permeter
选择阅读//返回IQueryable
这将导致每一米的读数只有最新和最旧的读数

我认为这是有效的原因是,它只需一次对DB的查找,就可以得到每一个仪表的所有读数,而不是每一个仪表的几次查找。我们有大约40000米,读数为30英里。我刚刚对我们的数据进行了查找,大约用了10秒

预执行的sql是最小和最大日期的两个子选择之间的交叉连接

更新:

因为这是可查询的,所以您应该能够在之后提供一个句点,如下所示:

var query = from reading in db.Readings
            group reading by reading.meterId
            into readingsPerMeter
            let oldestReadingPerMeter = readingsPerMeter.Min(g => g.time)
            let newestReadingPerMeter = readingsPerMeter.Max(g => g.time)
            from reading in readingsPerMeter
            where reading.time == oldestReadingPerMeter || reading.time == newestReadingPerMeter 
            select reading; //returns IQueryable<Reading> 
query.Where(r=>r.time > someTime1 && r.time < someTime2)
query.Where(r=>r.time>someTime1&&r.time

或者把它放到原来的查询中,我喜欢这样分开。查询尚未执行,因为我们尚未执行获取数据的操作。

感谢所有响应者,我得到了一些非常好的线索。 以下是对我有效的解决方案:

        /// <summary>
        /// Fills the result data with meter readings matching the filters.
        /// only take first and last reading for each meter in period.
        /// </summary>
        /// <param name="intervals">time intervals</param>
        /// <param name="meterIds">list of meter ids.</param>
        /// <param name="result">foreach meter id , a list of relevant meter readings</param>
        private void AddFirstLastReadings(List<RangeFilter<DateTime>> intervals, List<int> meterIds, Dictionary<int, List<MeterReading>> result)
        {
            foreach (RangeFilter<DateTime> interval in intervals)
            {
                var metersFirstAndLastReading = m_context.Meter.Where(m => meterIds.Contains(m.Id)).Select(m => new
                {
                    MeterId = m.Id,
                    FirstReading = m.MeterReading
                                    .Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
                                    .OrderBy(r => r.TimeStampLocal)
                                    .FirstOrDefault(),
                    LastReading = m.MeterReading
                                    .Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
                                    .OrderByDescending(r => r.TimeStampLocal)
                                    .FirstOrDefault()
                });

                foreach (var firstLast in metersFirstAndLastReading)
                {
                    MeterReading firstReading = firstLast.FirstReading;
                    MeterReading lastReading = firstLast.LastReading;

                    if (firstReading != null)
                    {
                        result[firstLast.MeterId].Add(firstReading);
                    }

                    if (lastReading != null && lastReading != firstReading)
                    {
                        result[firstLast.MeterId].Add(lastReading);
                    }

                }

            }
        }


    }
//
///用与过滤器匹配的仪表读数填充结果数据。
///仅读取周期内每米的第一个和最后一个读数。
/// 
///时间间隔
///仪表ID列表。
///对于每个仪表id,相关仪表读数的列表
私有void addFirstLastReaders(列出间隔、列出米数、字典结果)
{
foreach(以间隔为单位的范围过滤器间隔)
{
var metersfirstandlastreating=m_context.Meter.Where(m=>meterIds.Contains(m.Id))。选择(m=>new
{
MeterId=m.Id,
第一次读数=米读数
.Where(r=>r.TimeStampLocal>=interval.FromVal&&r.TimeStampLocalr.Time
var query = from reading in db.Readings
            group reading by reading.meterId
            into readingsPerMeter
            let oldestReadingPerMeter = readingsPerMeter.Min(g => g.time)
            let newestReadingPerMeter = readingsPerMeter.Max(g => g.time)
            from reading in readingsPerMeter
            where reading.time == oldestReadingPerMeter || reading.time == newestReadingPerMeter 
            select reading; //returns IQueryable<Reading> 
query.Where(r=>r.time > someTime1 && r.time < someTime2)
        /// <summary>
        /// Fills the result data with meter readings matching the filters.
        /// only take first and last reading for each meter in period.
        /// </summary>
        /// <param name="intervals">time intervals</param>
        /// <param name="meterIds">list of meter ids.</param>
        /// <param name="result">foreach meter id , a list of relevant meter readings</param>
        private void AddFirstLastReadings(List<RangeFilter<DateTime>> intervals, List<int> meterIds, Dictionary<int, List<MeterReading>> result)
        {
            foreach (RangeFilter<DateTime> interval in intervals)
            {
                var metersFirstAndLastReading = m_context.Meter.Where(m => meterIds.Contains(m.Id)).Select(m => new
                {
                    MeterId = m.Id,
                    FirstReading = m.MeterReading
                                    .Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
                                    .OrderBy(r => r.TimeStampLocal)
                                    .FirstOrDefault(),
                    LastReading = m.MeterReading
                                    .Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
                                    .OrderByDescending(r => r.TimeStampLocal)
                                    .FirstOrDefault()
                });

                foreach (var firstLast in metersFirstAndLastReading)
                {
                    MeterReading firstReading = firstLast.FirstReading;
                    MeterReading lastReading = firstLast.LastReading;

                    if (firstReading != null)
                    {
                        result[firstLast.MeterId].Add(firstReading);
                    }

                    if (lastReading != null && lastReading != firstReading)
                    {
                        result[firstLast.MeterId].Add(lastReading);
                    }

                }

            }
        }


    }