繁体   English   中英

从IQueryable创建自定义对象,而无需将所有内容加载到内存中

[英]Create custom objects from IQueryable without loading everything into memory

这是对该问题的跟进问题 您应该先阅读。

现在,由于这个答案 ,我创建了一个查询,该查询将返回正确的条目。 看到:

IQueryable<Data> onePerHour = dataLastWeek
    .Where(d => 
        !dataLastWeek
        .Any(d2 =>
            d2.ArchiveTime.Date == d.ArchiveTime.Date &&
            d2.ArchiveTime.Hour == d.ArchiveTime.Hour &&
            d2.ArchiveTime < d.ArchiveTime));

现在,为了处理条目并将它们显示在图表上,我只需要模型类Data一个或两个属性。 用例是这样的:

List<Data> actualData = onePerHour.ToList();

var tempCTupels = new List<TimeTupel<float>>();
tempCTupels.AddRange(actualData.Select(d => new TimeTupel<float>(d.ArchiveTime, d.TempC)));

var co2Tupels = new List<TimeTupel<float>>();
tempCTupels.AddRange(actualData.Select(d => new TimeTupel<float>(d.ArchiveTime, d.CO2Percent)));

TimeTupel非常简单,定义如下:

public class TimeTupel<TData>
{
    public TimeTupel(DateTime time, TData yValue)
    {
        Time = time;
        YValue = yValue;
    }

    public DateTime Time { get; set; }
    public TData YValue { get; set; }
}

当前, actualdata是一个List<Data> ,这意味着它已完全加载到内存中。
由于我仅使用两个属性,因此无需检索整个对象即可创建TimeTupel

现在我的问题是如何实现性能提升? 删除ToList是正确的方法吗?

我尝试过的事情

  • 只需使用IQueryable<Data>创建TimeTupel
    IQueryable<Data> actualData = onePerHour; 产生运行时错误(“ System.InvalidOperationException:'Sql Tree中的Null TypeMapping'”)

  • 使用AsEnumerable
    IEnumerable<Data> actualData = onePerHour.AsEnumerable(); 速度很慢,大约需要22秒才能处理10天的数据

  • 如上面的代码所示,使用ToListToArray几乎相等):
    List<Data> actualData = onePerHour.ToList(); 更快,相同数量的数据大约需要5秒

您可以在Select语句中使用匿名类型,以仅将所需的数据列检索到内存中,然后从该处将内存中的数据转换为TimeTupel <>类。 它看起来像这样:

var actualData = dataLastWeek
    .Where(d => 
        !dataLastWeek
        .Any(d2 =>
            d2.ArchiveTime.Date == d.ArchiveTime.Date &&
            d2.ArchiveTime.Hour == d.ArchiveTime.Hour &&
            d2.ArchiveTime < d.ArchiveTime))
    .Select(d => new { d.ArchiveTime, d.TempC, d.CO2Percent})
    .ToList();

var tempCTupels = actualData.Select(d => new TimeTupel<float>(d.ArchiveTime, d.TempC)).ToList();

var co2Tupels = actualData.Select(d => new TimeTupel<float>(d.ArchiveTime, d.CO2Percent)).ToList();

在实际从IQueryable加载对象之前,您只能选择所需的属性。 在Where语句后使用Select仅加载所需的内容。

一个例子:

假设您有一个看起来像这样的课:

public class Person {
        public string Name { get; set; }
        public int Age { get; set; }
    }

我可以初始化要测试的项目列表:

var people = new List<Person> { new Person { Name = "John", Age = 10 }, new Person { Name = "Archie", Age = 40 } };

然后我们应用一个过滤器:

var filterred = people.Where(p => p.Age > 15).Select(p => p.Name).ToList();

如果我想使用选择创建一个新对象,然后选择一个以上的属性,则可以执行以下操作:

var objFilterred = people.Where(p => p.Age > 15).Select(p => new { FullName = p.Name  }).ToList();

您不必使用匿名对象,也可以创建一个仅包含所需属性的新类,然后直接填充该类。

您无法“删除” ToList,因为这实际上是在执行查询。 IQueryable不是数据,它是尚未运行的查询,您可以根据需要链接任意数量的内容。 最后一步是执行它,运行类似于ToList的操作,以实际加载对象。 只要构建了IQueryable,并在完成后执行它,那么执行速度就应该有所提高

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM