使用 Linq to Entities 在一次操作中获取 COUNT 和 SKIP TAKE

Posted

技术标签:

【中文标题】使用 Linq to Entities 在一次操作中获取 COUNT 和 SKIP TAKE【英文标题】:Getting COUNT and SKIP TAKE in one operation with Linq to Entities 【发布时间】:2015-03-23 18:54:30 【问题描述】:

我在 Linq to Entities 支持的数据访问层中有一个数据调用,旨在进行分页调用。

在此过程中,我需要选择数据的一个子集,比如 50 行,但还要获取所有匹配项的计数,以了解要分页的总匹配项数。

目前,我正在做以下事情:

var queryResult = DatabaseContext.Table
    .Where(x => !x.IsDeleted)
    .Where(p => (
            p.PropertyOne.ToLower().Contains(query) ||
            p.PropertyTwo.ToLower().Contains(query) 
            ));

int count = queryResult.Count();

var returnData = queryResult
    .OrderBy(i => i.ID)
    .Skip(start).Take((length))
    .Select(y => new ObjectDTO
    
        PropertyOne = y.PropertyOne,
        PropertyTwo = y.PropertyTwo
    
    .AsEnumerable();

这会导致两个代价高昂的数据库操作。出于某种原因,COUNT 操作实际上比SELECT 操作花费的时间更长。

有没有办法在同一操作中获取计数和子集?

对我来说,逻辑流程表明我们执行以下操作:

看表 在表中查找符合条件的项目 获取所有匹配项的计数 返回匹配的编号子集

这在一个操作中似乎是可能的,但我不知道如何。

尝试一,慢

尝试了 D Stanley 将完整结果集转换为 List 并在分页中进行计数和内存的建议,但它的速度大约慢了 2 倍(平均 6.9 秒 vs 平均 3.9 秒)

值得一提的是,该数据集大约有 25,000 条记录,在 JOIN 中搜索了十几个相关表。

【问题讨论】:

如果将queryResult 枚举为List<T> 并在列表中使用.Count,会更快吗? 仅供参考,计数需要更长的时间,因为它必须遍历表中的所有行,而第二个行一旦得到 start + length 匹配就可以停止。 @diemaus 这将提取比需要更多的数据,并且可能会更慢,具体取决于数据量。 @juharr 正如您所说,这取决于您要检索的数据量。为什么不试试呢? :) 您可以尝试扩展实体框架github.com/loresoft/EntityFramework.Extended 这将允许您在对数据库的一次调用中执行这两项操作。考虑到查询的性质,它仍然不太可能很快。 【参考方案1】:

这可能是可能的,但由于您使用的标准,它可能不会更快。由于您在列值中搜索文本,因此不能使用索引,因此必须进行表扫描。您可以执行单个查询以获取所有记录并在 linq-to-objects 中执行 CountSkip/Take

var queryResult = DatabaseContext.Table
    .Where(x => !x.IsDeleted)
    .OrderBy(i => i.ID)
    .Where(p => (
            p.PropertyOne.ToLower().Contains(query) ||
            p.PropertyTwo.ToLower().Contains(query) 
            ))
    .ToList();

int count = queryResult.Count();  // now this will be a linq-to-objects query

var returnData = queryResult
    .Skip(start).Take((length))
    .AsEnumerable();

但您必须尝试一下,看看它是否会更快。

【讨论】:

但是,如果表格中的行数不多,这当然会出现问题。 可能,但在数据库上执行两次表扫描可能仍然更快。 一旦你调用了.ToList(),你就会在内存中得到一个列表。只需访问Count 属性而不是调用.Count() 方法——尽管LINQ 应该看到底层属性并使用它。 唯一的缺点是我使用未显示的 SELECT 语句强制转换为数据传输对象。这可能会改变一些事情。让我更新一下问题。 不幸的是@DStanley,在大多数表上,投射到列表并在内存中过滤实际上更慢。

以上是关于使用 Linq to Entities 在一次操作中获取 COUNT 和 SKIP TAKE的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to Entities 查询注意事项

LINQ to Entities 查询注意事项

LINQ to Entities 查询注意事项

通过接口属性 LINQ to Entities

LINQ to Entities 无法识别方法“System.Web.Mvc.FileResult”

使用 LINQ-to-Entities 搜索时忽略空字符串