从 IQueryable 创建自定义对象,无需将所有内容加载到内存中
Create custom objects from IQueryable without loading everything into memory
这是 的后续问题。你应该先读一下。
感谢 ,我现在创建了一个查询,该查询将 return 正确的条目。参见:
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));
现在为了处理条目并将它们显示在图表上,我只需要模型的一个或两个属性 class 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
s。
现在我的问题是如何实现性能提升?删除 ToList
是正确的方法吗?
我尝试过的事情
只需使用 IQueryable<Data>
创建 TimeTupel
:
IQueryable<Data> actualData = onePerHour;
产生运行时错误 ("System.InvalidOperationException: 'Null TypeMapping in Sql Tree'")
使用AsEnumerable
:
IEnumerable<Data> actualData = onePerHour.AsEnumerable();
很慢,10 天的数据大约需要 22 秒
如上面代码所示使用ToList
(ToArray
几乎等于):
List<Data> actualData = onePerHour.ToList();
更快,相同数量的数据大约需要 5 秒
您可以在 Select 语句中使用匿名类型来仅将需要的数据列检索到内存中,然后将该 in-memory 数据转换为 TimeTupel<> class从那里。它看起来像这样:
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 加载对象之前,您可以 select 只有您需要的属性。在 Where 语句之后使用 Select 以仅加载您需要的内容。
一个例子:
假设您有一个 class 如下所示:
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();
如果我想用 selection 创建一个新对象,select 多于一个 属性,我可以这样做:
var objFilterred = people.Where(p => p.Age > 15).Select(p => new { FullName = p.Name }).ToList();
您不必使用匿名对象,您也可以创建一个新的 class 只包含您需要的属性并简单地填充它。
您不能 "remove" ToList,因为这是实际执行查询的内容。 IQueryable 不是数据,它是一个尚未 运行 的查询,您可以根据需要链接任意数量的内容。最后一步是执行它,运行ning 类似 ToList 的东西,以实际加载对象。只要您构建 IQueryable 并在完成后执行它,那么您应该会看到执行速度的提高
这是
感谢
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));
现在为了处理条目并将它们显示在图表上,我只需要模型的一个或两个属性 class 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
s。
现在我的问题是如何实现性能提升?删除 ToList
是正确的方法吗?
我尝试过的事情
只需使用
IQueryable<Data>
创建TimeTupel
:
IQueryable<Data> actualData = onePerHour;
产生运行时错误 ("System.InvalidOperationException: 'Null TypeMapping in Sql Tree'")使用
AsEnumerable
:
IEnumerable<Data> actualData = onePerHour.AsEnumerable();
很慢,10 天的数据大约需要 22 秒如上面代码所示使用
ToList
(ToArray
几乎等于):
List<Data> actualData = onePerHour.ToList();
更快,相同数量的数据大约需要 5 秒
您可以在 Select 语句中使用匿名类型来仅将需要的数据列检索到内存中,然后将该 in-memory 数据转换为 TimeTupel<> class从那里。它看起来像这样:
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 加载对象之前,您可以 select 只有您需要的属性。在 Where 语句之后使用 Select 以仅加载您需要的内容。
一个例子:
假设您有一个 class 如下所示:
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();
如果我想用 selection 创建一个新对象,select 多于一个 属性,我可以这样做:
var objFilterred = people.Where(p => p.Age > 15).Select(p => new { FullName = p.Name }).ToList();
您不必使用匿名对象,您也可以创建一个新的 class 只包含您需要的属性并简单地填充它。
您不能 "remove" ToList,因为这是实际执行查询的内容。 IQueryable 不是数据,它是一个尚未 运行 的查询,您可以根据需要链接任意数量的内容。最后一步是执行它,运行ning 类似 ToList 的东西,以实际加载对象。只要您构建 IQueryable 并在完成后执行它,那么您应该会看到执行速度的提高