EntityFramework - 仅从集合中加载最后一个子项(性能问题)

Posted

技术标签:

【中文标题】EntityFramework - 仅从集合中加载最后一个子项(性能问题)【英文标题】:EntityFramework - load only last child from collection (performance issue) 【发布时间】:2017-07-12 14:59:29 【问题描述】:

这是我的模型:

public partial class auctions

    public int id  get; set; 
    public virtual ICollection<auction_data> auction_data  get; set; 

public partial class auction_data

    public int id  get; set; 
    public int auction_id  get; set; 
    public string title  get; set; 
    public virtual auctions auctions  get; set; 

每次拍卖都可能附有数千个拍卖数据项。 但我只对最后一个感兴趣:

var matchingAuctions = context.auctions.Where(a => /** some conditions **/);
foreach (auctions auction in matchingAuctions)

    var data = auction.auction_data.LastOrDefault(); // <---- takes very long time and memory when having large count of auction_data

似乎 EF 将所有拍卖数据加载到内存中,然后返回最后一个。 问题是:如何提高此代码的性能并避免将所有这些记录加载到内存中?

编辑:一些时机

DateTime t1 = DateTime.Now;
var data = auction.auction_data.LastOrDefault();
DateTime t2 = DateTime.Now;
TimeSpan diff = t2 - t1; // 8.8s


DateTime t1 = DateTime.Now;
var data = auction.auction_data.OrderByDescending(a => a.id).FirstOrDefault();
DateTime t2 = DateTime.Now;
TimeSpan diff = t2 - t1; // 8.7s

DateTime t1 = DateTime.Now;
var data = auction.auction_data.Last();
DateTime t2 = DateTime.Now;
TimeSpan diff = t2 - t1; // 8.7s

int id = auction.id;
DateTime t1 = DateTime.Now;
var data = context.auction_data.Where(d => d.auction_id == id).OrderByDescending(d => d.id).FirstOrDefault();
DateTime t2 = DateTime.Now;
TimeSpan diff = t2 - t1; // 0.06s

【问题讨论】:

您可以反转排序,然后使用First() 而不是Last() 在没有应用排序的情况下,为什么你会选择最后一行? 定义...最后一个? (最高 ID/最新日期时间?) 也尝试过这种方式,但得到了相同的结果 我认为不是..auction.auction_data.OrderByDescending(a =&gt; /** column name **/).FirstOrDefault() 不会检索所有行... 最后一个元素是插入到表中的最后一个元素(它的 ID 最高) 【参考方案1】:

这很正常。当您编写auction.auction_data 时,它会读取所有的auction.auction_data。如果我是你,我会这样做以提高性能:

public partial class auctions

  public int id  get; set; 
  public int lastDataId  get; set;
  public virtual ICollection<auction_data> auction_data  get; set; 

在插入数据时,将 id 作为最后一个插入。

作为测试:我没有要测试的大型数据库,但也许更改 Last() 而不是 LastOrDefault() 可能会让你失望。我读到了:

LastOrDefault 是一个有用的方法。它找到集合中匹配条件的最后一个元素

所以为了测试是否有条件,它需要读取所有记录,但是 也许 Last() 不需要

【讨论】:

我从一开始就不相信OrderByDescending。这几乎是一样的。但我建议的最后一种(好)方法,你需要有数据ID。 ? 还有一点:var data = context.auction_data.Where(d =&gt; d.auction_id == id).OrderByDescending(d =&gt; d.id).FirstOrDefault();不需要排序 id为主键时只有一个元素【参考方案2】:

变化:

var data = auction.auction_data.LastOrDefault();

到:

var data = auction.auction_data.OrderByDescending(x => x.id).FirstOrDefault();

【讨论】:

我试过这种方式,但几乎没有区别(在我的测试用例中是 8.8 秒 vs 8.7 秒) 你有适当的索引吗?我想知道,因为当你的数据库表被正确索引时,EF 确实表现得很好。 是的,我在 ID 上设置了主键。正如我所提到的,问题是 EF 将整个表读入内存,然后返回记录,而不是只从表中读取一行

以上是关于EntityFramework - 仅从集合中加载最后一个子项(性能问题)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Boot 进行测试时仅从测试/资源中加载数据

在 NHibernate 中加载没有重复的多级集合

快速在视图控制器中加载集合视图控制器

在类中初始化时未从设置中加载集合

从预先加载的 laravel 集合中加载特定的列

在 Magento 中加载日志访问者集合时收到“查询为空”错误