必须在大表上的 .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()的主要内容,如果未能解决你的问题,请参考以下文章