Пакет SDK для Azure Cosmos C # версии 3 возвращает недопустимый маркер продолжения

Я работаю над переносом приложения из Cosmos C # SDK v2 в v3. Я столкнулся с препятствием с разбивкой на страницы и, кажется, не могу этого понять.

У меня есть запрос, который будет искать конкретный контейнер и разбивать запросы на страницы. Мой код настроен для обработки размера страницы (maxItemCount) и токена продолжения. Кроме того, отлично работают первые несколько запросов. Но когда мне нравится четвертая страница, токен продолжения, который я получаю в ответе от Cosmos, недействителен. Это выглядит так:

[{\"range\":{\"min\":\"05C1DFFFFFFFFC\",\"max\":\"FF\"}}]

И когда я передаю это следующему запросу на захват следующей страницы, я получаю следующую ошибку:

Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: ; Reason: (CompositeContinuationToken is missing field: 'token': {"range":{"min":"05C1DFFFFFFFFC","max":"FF"}}););););););););

Теперь я обнаружил, что если я изменяю токен продолжения перед его отправкой, я могу добиться успеха запроса. Для этого мне нужно добавить свойство в строку JSON, как вы уже догадались, token.

Вот рассматриваемый код, который обрабатывает этот запрос:

            IQueryable<LeadEntity> query;
            var queryResults = new List<LeadEntity>();

            var requestOptions = new QueryRequestOptions
            {
                MaxItemCount = input.Page?.Size ?? Core.Shared.Constants.PageSize.Default
            };

            if (input.Page?.ContinuationToken == "" || input.Page?.ContinuationToken == null) 
            {
                query = Container.GetItemLinqQueryable<LeadEntity>(false, null, requestOptions);
            }
            else 
            {
                query = Container.GetItemLinqQueryable<LeadEntity>(false, input.Page.ContinuationToken, requestOptions);
            }

            if (input.Criteria?.CallKeys.IsActive ?? false)
            {
                var callKeys = (input.Criteria.CallKeys?.Value ?? Enumerable.Empty<Guid>())
                    .Select(a => a.ToString());

                query = query.Where(entity => callKeys.Contains(entity.CallKey));
            }

            if (input.Criteria?.AdvisorOids.IsActive ?? false)
            {
                var advisorOids = (input.Criteria.AdvisorOids?.Value ?? Enumerable.Empty<Guid>())
                    .Select(a => a.ToString());

                query = query.Where(entity => advisorOids.Contains(entity.AdvisorOid));
            }

            if (input.Criteria?.CreatedOn.IsActive ?? false)
            {
                var start = input.Criteria.CreatedOn.Value.Start;
                var duration = input.Criteria.CreatedOn.Value.Duration;
                var end = input.Criteria.CreatedOn.Value.End;

                if (start.HasValue && end.HasValue)
                {
                    query = query.Where(entity => start.Value <= entity.Created.On && entity.Created.On <= end.Value);
                }
                else if (start.HasValue)
                {
                    query = query.Where(entity => start.Value <= entity.Created.On && entity.Created.On <= start.Value.Add(+duration));
                }
                else if (end.HasValue)
                {
                    query = query.Where(entity => end.Value >= entity.Created.On && entity.Created.On >= end.Value.Add(-duration));
                }
                else
                {
                    query = query.Where(entity => false);
                }
            }

            if (input.Criteria?.HasPortalKey.IsActive ?? false)
            {
                query = query.Where(entity => input.Criteria.HasPortalKey.Value
                        && entity.PortalKey != null
                        && entity.PortalKey != default(Guid).ToString()
                );
            }

            query = query.Where(a => a.IsRemoved.IsDefined() ? !a.IsRemoved : true);

            var totalCount = await query.CountAsync(ct);

            query = input.Orderings?.Any() ?? false
                ? query.OrderBy(string.Join(",", input.Orderings))
                : query;

            query = input.Limit.HasValue
                ? query.Take(input.Limit.Value)
                : query;

            var feedIterator = query.ToFeedIterator();

            FeedResponse<LeadEntity> feedResults = await feedIterator.ReadNextAsync(ct);
            
            queryResults.AddRange(feedResults);

            Console.WriteLine($"[LeadSearchAsync] total operation cost: {feedResults.RequestCharge} RUs");

            var output = new PageOutput<LeadOutput>
            {
                Items = Mapper.Map<IEnumerable<LeadOutput>>(queryResults),
                ContinuationToken = feedResults.ContinuationToken,
                TotalCount = totalCount
            };

            return output;

Я искал в Google свою задницу и нашел только несколько случаев, когда это происходило, и обычно это на v2 sdk, а не на v3. Часть меня хочет думать, что я делаю неправильную разбивку на страницы, но, похоже, по большей части это работает.

Любая помощь в решении этой проблемы будет принята с благодарностью. Я не нашел много документации по v3 SDK, касающейся разбивки на страницы, и она явно отличается от v2.


person Nate    schedule 21.07.2020    source источник


Ответы (2)


OK. Сегодня, погуглив еще раз, я обнаружил эту проблему Github < / а>.

В основном у меня был этот блок кода в моем запуске, который установил JSONSerializerSettings по умолчанию для игнорирования нулевых значений:

var JsonSerializerSettings = new JsonSerializerSettings 
{
    DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
    DateParseHandling = DateParseHandling.DateTimeOffset,             
    NullValueHandling = NullValueHandling.Ignore,             
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};

JsonSerializerSettings.Converters.Add(new StringEnumConverter());
JsonConvert.DefaultSettings = () => JsonSerializerSettings;

Это заставило мою строку токена продолжения удалить поле token, потому что оно было нулевым. По-видимому, совершенно нормально иметь поле нулевого токена в вашем токене продолжения.

Если я ошибаюсь в вышеприведенном предположении, сообщите мне. Потому что на полпути к разбиению на страницы я получаю токен, который выглядит так:

[{\"token\":null,\"range\":{\"min\":\"05C1DFFFFFFFFC\",\"max\":\"FF\"}}]

В любом случае, я отмечаю это как решенное самостоятельно. Надеюсь, это поможет кому-то еще с той же проблемой.

person Nate    schedule 22.07.2020
comment
Это помогло мне. Спасибо! - person Nate; 05.08.2020

 IQueryable<returnVModel> query;
 var requestOptions = new QueryRequestOptions
 {
     MaxItemCount = 20
 };
 if (Token == "" || Token == null)
 {
        query = Container.GetItemLinqQueryable<returnVModel>(false, null, requestOptions).Where(x => x.id == id);
 }
 else
 {
        query = Container.GetItemLinqQueryable<returnVModel>(false, Token, requestOptions).Where(x => x.id == id);
 }
 var ct = new CancellationTokenSource();
 var totalCount = await query.CountAsync(ct.Token); //Total Count
 var feedIterator = query.ToFeedIterator();
 var queryResults = new List<returnVModel>();
 FeedResponse<returnVModel> feedResults = await feedIterator.ReadNextAsync(ct.Token);
 queryResults.AddRange(feedResults); // Output
 var PaginationToken = feedResults.ContinuationToken //Token

@Nate Мне сослались на ваш код и я реализовал разбиение на страницы. В моей разработке он работал нормально. Могу ли я столкнуться с какой-либо проблемой после перехода в производственную среду.

person Prince Antony G    schedule 12.02.2021
comment
@Nate Будет ли у нас возникать такая проблема с производительностью, когда мы перейдем к производству? - person Prince Antony G; 12.02.2021