【问题标题】:LINQ .Take() returns more elements than requestedLINQ .Take() 返回比请求更多的元素
【发布时间】:2012-08-22 10:25:56
【问题描述】:

我们有一个简单的 LINQ-to-Entities 查询,它应该从特定页面返回特定数量的元素。 请求的示例可以是:

var query = from r in records
            orderby r.createdDate descending
            select new MyObject()
            { ... };

//Parameters: pageId = 8, countPerPage = 10
List<MyObject> list = query.Skip(pageId * countPerPage).Take(countPerPage);

上面的例子在大多数情况下都很好用,但有时列表有超过 10 个元素。这似乎并不总是正确的,并且取决于数据库数据。 例如,当我们请求页面 10 并将 countPerPage 传递为 10 时,我们将获得 10 个元素。但是当我们请求第 12 页并将 countPerPage 作为 10 传递时,我们将获得 11 个元素。然后当我们请求第 21 页时,我们又得到了 10 个元素。

发生这种情况有什么可能的原因吗?

更新: 当然,查询并不像示例中那样简单,并且包含子查询。

还有一个更完整的例子:

var elementsQuery = from m in entityContext.elements
                    where m.elementSearchText.Contains(filter)
                    orderby m.CreatedDate descending
                    select new DataContracts.ElementForWeb()
                    {
                        FirstName = m.FirstName,
                        LastName = m.LastName,
                        Photos = (from p in m.Photos select p.ID),
                        PlacesCount = m.Childs.Where(x => x.Place != null).Count() + ((m.MainChild != null)?1:0),
                        SubElements = (
                            from t in m.Childs
                            orderby t.CreatedDate descending
                            select new DataContracts.ChildForWeb()
                            {
                                CommentsCount = t.ChildComments.Count,
                                Photos = (from p in t.Photos select p.ID),
                                Comments = (from c in t.ChildComments
                                orderby c.CreatedDate descending
                                select new DataContracts.CommentForWeb()
                                {
                                    CommentId = c.ID,
                                    CommentText = c.CommentText,
                                    CreatedByPhotoId = c.Account.UserPhoto,
                                    CreatedDate = c.CreatedDate,
                                }).Take(5)
                            }).Take(5)
                      };

List<DataContracts.ElementForWeb> elements = 
    new List<DataContracts.ElementForWeb>(
        elementsQuery
           .Skip(pageId * countPerPage)
           .Take(countPerPage));

UPDATE2:这是更有趣的测试。

        for (var i = 0; i < 10; i++) {
            Service.GetElementsForWebPaged(12, 10, "",
                function (result) {
                    console.log("Elements returned: " + result.length);
                },
                function (error) {
                });
        }

结果“棒极了”!

Elements returned: 11
Elements returned: 11
Elements returned: 10
Elements returned: 11
Elements returned: 11
Elements returned: 10
Elements returned: 11
Elements returned: 10
Elements returned: 11
Elements returned: 11

【问题讨论】:

  • 这很奇怪;这是您选择语句的完整扩展吗?你能提供任何样本数据吗?
  • 这很奇怪。你确定你没有参加一个小组,然后分拆,例如在“有关系”类型的场景中? stackoverflow.com/questions/1342848/…
  • 生成的 SQL 是什么样的?
  • Linq to entity 使用 Row_Number() 进行分页,对吧?如果您的日期完全相同,则可能会发生一些不确定的事情。尝试添加第二列来排序,例如键或其他内容,即:orderby m.CreatedDate descending, m.ElementId.
  • .ToList() 更像是一个调试建议而不是生产建议 ;)

标签: c# linq entities


【解决方案1】:

很难测试这个答案,因为它取决于您的架构和测试数据等。 但我相信您在将 IQueryAble 结果与 IEnumerable 结果混合时可能会遇到问题。

请记住,在完成 foreach 或 ToList() 之前,linq-To-Entities 查询实际上不会往返于数据库。

我建议先把它分成几块:

var elementsQuery = from m in entityContext.elements
                    where m.elementSearchText.Contains(filter)
                    orderby m.CreatedDate descending;

var elements = elementsQuery.Skip(pageId * countPerPage).Take(countPerPage)).ToList();

然后构建你的投影......

var elementsForWeb = from m in elements
                     select new DataContracts.ElementForWeb()
                     {
                     ...
                     }

【讨论】:

  • 这就是我最终所做的。但是,这意味着我们对数据库的往返次数要多得多(针对结果列表中的每个元素),这实际上可能比传输额外数据的成本更高。
  • 是的。可能。有一些非常好的工具可用于分析生成的数据库 SQL。 (这里的好文章)msdn.microsoft.com/en-us/magazine/gg490349.aspx 当你循环遍历 elementsForWeb 时,这将是内存中的列表。但是,您在投影中执行的查询将不会发生,直到您已经 foreached 或 ToListed 等,该组项目。
  • 我接受这个答案,因为它与我们最终所做的很接近。不过,我正在考虑将问题发布到 Microsoft Connect。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-14
  • 2014-10-08
  • 2019-04-22
  • 2022-10-30
  • 1970-01-01
  • 2019-05-31
相关资源
最近更新 更多