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

问题描述 投票:0回答:3

我在 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 中搜索的相关表。

c# sql-server linq entity-framework entity-framework-6
3个回答
0
投票

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

Count
Skip/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();

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


0
投票

这样的事情怎么样:

db.Products
   .Take(10)
   .Select(p => new 
                {
                    Total = db.Products.Count, 
                    Product = p
                })

如果这不好,了解一下在 SQL 中,您可以使用 OVER() 获得总结果和其中一页可能会有所帮助。


0
投票

我自己也非常努力地寻找这个问题的答案,但没有成功。

我认为这是 LINQ to Entities 的限制,并制作了一个存储过程,而不是在 LINQ 上花费更多时间。

在存储过程中,我会做这样的事情

--Get expensive query into temp table
SELECT *
INTO #t
FROM table
WHERE <expensive query here>

--Set the output param of your stored procedure
SET @totalRows = (SELECT COUNT(*) FROM #t);

--Select your final result set
SELECT *
FROM #t
OFFSET (0) ROWS
FETCH NEXT (50) ROWS ONLY;

--Cleanup
DROP TABLE IF EXISTS #t;
© www.soinside.com 2019 - 2024. All rights reserved.