Подтвердить что ты не робот

LINQ.Take() возвращает больше элементов, чем запрошено

У нас есть простой запрос 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 элементов.

Есть ли возможная причина, почему это происходит?

UPDATE: Запрос, конечно, не так прост, как он есть в примере, и содержит подзапросы.

И вот более полный пример:

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
4b9b3361

Ответ 1

Трудно проверить этот ответ, потому что он зависит от вашей схемы и тестовых данных и т.д. Но я считаю, что у вас может возникнуть проблема с смешиванием результатов IQueryAble с результатами IEnumerable.

Помните, что запрос linq-To-Entities фактически не выполняет обратную пересылку в базу данных до тех пор, пока не будет выполнено foreach или ToList().

Я предлагаю сначала разбить это на части:

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()
                     {
                     ...
                     }