必须在大表上的 .Skip() 和 .Take() 之前在实体框架 4.1 中调用 .ToList()

Posted

技术标签:

【中文标题】必须在大表上的 .Skip() 和 .Take() 之前在实体框架 4.1 中调用 .ToList()【英文标题】:Having to call .ToList() in entity framework 4.1 before .Skip() and .Take() on large table 【发布时间】:2012-04-12 04:15:36 【问题描述】:

我正在尝试用我的应用做一些聪明的事情。我有一张表,广告 - 其中包含汽车信息:型号、里程等。该表通过外键与其他一些表相关,例如通过链接到“VehicleModels”表等的外键检索模型名称。

在应用程序的“实体”目录(映射到数据库中表的类)中,我有一个用于 Adverts 表的 Advert.cs。这有几个 EF 被告知忽略的属性(在 fluent api 中),因为它们没有映射到 Adverts 表中的实际字段。

这些字段背后的想法是将计算出的与用户输入的邮政编码(邮政编码)的距离存储在搜索表单中,如果他们只想查看特定半径内的可用汽车,则该搜索表单会过滤广告表。例如:

IQueryable<Advert> FilteredAdverts = repository.Adverts
.Where(am => mfr == "" || am.Manufacturer == mfr) &&
    (am => model == etc etc...)

稍后,计算代码的相似距离:

if (userPostcode != null) 
    foreach (var ap in FilteredAdverts.ToList()) 
        distmiles = //calculate distance in miles
        distkm = //calculate distance in km

        ap.DistanceMiles = Convert.ToInt32(distmiles);
        ap.DistanceKm = Convert.ToInt32(distkm);
    

我遇到的问题是,为了将值分配给这两个字段,我必须使用 .ToList() 从表中提取所有行。如果只有几行可以正常工作,但是当有 ~1,000 行时,它需要大约。 2.2 秒,当我将其增加到大约 12,000 行时,当没有应用过滤器(即返回所有活动广告)时,页面加载需要 32 秒。

我在调用 .Skip 和 .Take 之前拉出所有广告以显示它们的原因是,搜索表单中可用的过滤器基于所有当前活动广告的可能选项,即剩余时间,而不仅仅是从制造商表中选择制造商列表(用户可以选择没有搜索结果的制造商)。例如

VehicleManufacturers = (from vm in FilteredAdverts.Select(x => x.VehicleManufacturer).Distinct().OrderBy(x => x)
    select new SearchOptionsModel
    
            Value = vm,
            Text = vm,
            Count = FilteredAdvertsVM.Where(x => x.VehicleManufacturer == vm).Count(),
    )

    .... filters for model, mileage etc

要了解我想要实现的目标 - 请查看 Autotrader 网站上的搜索表单。

一旦应用了所有过滤器,就在将模型传递给视图之前,应用了 .Skip 和 .Take,但当然此时所有行都已被拉出。

我的问题是,我该如何重做这个?有没有更好的方法在我的 Advert 实体类中使用这些非映射属性?我在我的家用电脑上工作 - C2D @ 3.4GHz,2GB 内存 - 慢速查询可以在财产网络主机上运行吗?

【问题讨论】:

【参考方案1】:

您不能在客户端功能上使用服务器端分页。这是简短的答案。假设我正确理解您的需求(根据与给定邮政编码的接近程度过滤列表),我过去使用的解决方案是将每个“广告”记录存储为该记录的邮政编码的纬度/经度。该数据被持久化。

然后,在查询的时候,根据到中心的X距离(用户提供的邮政编码)计算一个边界框(lat1,lng1,lat2,lng2),并根据lat/lng的记录过滤查询结果适合这个盒子。然后,您可以应用客户端计算来进一步、更准确地过滤列表,但使用此方法,您可以建立一个基本过滤器以最大限度地减少提取的记录数。

编辑:您可以根据到中心点的绝对距离对查询结果进行排序,以 abs(latU-latR) 和 abs(lngU-lngR) 表示,其中 latU/lngU 是用户的 lat/lng提供的邮政编码和 latR/lngR 是数据库中记录的 lat/lng。

【讨论】:

以上是关于必须在大表上的 .Skip() 和 .Take() 之前在实体框架 4.1 中调用 .ToList()的主要内容,如果未能解决你的问题,请参考以下文章

executeUpdate 在大表上返回负值

如何在大表上优化这个 mysql 连接?

MySQL查询在大表上很慢

尽可能快地获取我的 MySQL 的第一行(也在大表上)

在大表上使用 Django-Filter 以及 DataTables2

经验分享为什么hive在大表上加条件后执行limit很慢