public async Task <JsonResult> SearchDevicesHistoryByPage(string deviceId = null, int batchIndex = 1, int batchSize = 200, string startTimestamp = null)
        {
            // Manage session and Context
            HttpServiceUriBuilder contextUri     = new HttpServiceUriBuilder().SetServiceName(this.context.ServiceName);
            DeviceEventRowList    deviceMessages = new DeviceEventRowList(batchIndex, batchSize);

            ServiceUriBuilder uriBuilder = new ServiceUriBuilder(Names.InsightDataServiceName);
            Uri serviceUri = uriBuilder.Build();

            // service may be partitioned.
            // this will aggregate the queue lengths from each partition
            ServicePartitionList partitions = await this.fabricClient.QueryManager.GetPartitionListAsync(serviceUri);

            foreach (Partition partition in partitions)
            {
                string pathAndQuery = $"/api/devices/history/batchIndex/{batchIndex}/batchSize/{batchSize}";

                if (startTimestamp != null)
                {
                    pathAndQuery = $"/api/devices/history/batchIndex/{batchIndex}/batchSize/{batchSize}/startingAt/{startTimestamp}";
                    deviceMessages.SearchStartTimestamp = DateTimeOffset.Parse(startTimestamp).ToUniversalTime();
                }

                Uri getUrl = new HttpServiceUriBuilder()
                             .SetServiceName(serviceUri)
                             .SetPartitionKey(((Int64RangePartitionInformation)partition.PartitionInformation).LowKey)
                             .SetServicePathAndQuery(pathAndQuery)
                             .Build();

                HttpResponseMessage response = await httpClient.GetAsync(getUrl, appLifetime.ApplicationStopping);

                if (response.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    JsonSerializer serializer = new JsonSerializer();
                    using (StreamReader streamReader = new StreamReader(await response.Content.ReadAsStreamAsync()))
                    {
                        using (JsonTextReader jsonReader = new JsonTextReader(streamReader))
                        {
                            DeviceEventRowList resultDeviceEventRowList = serializer.Deserialize <DeviceEventRowList>(jsonReader);

                            foreach (DeviceEventRow row in resultDeviceEventRowList.Rows)
                            {
                                deviceMessages.AddRow(row);
                            }
                            deviceMessages.TotalCount += resultDeviceEventRowList.TotalCount;

                            if (deviceMessages.SearchStartTimestamp.ToUnixTimeMilliseconds() < 1000)
                            {
                                deviceMessages.SearchStartTimestamp = resultDeviceEventRowList.SearchStartTimestamp;
                            }
                        }
                    }
                }
            }

            return(this.Json(deviceMessages));
        }
        public async Task <JsonResult> SearchDevicesHistoryByPage(string deviceId = null, int batchIndex = 1, int batchSize = 200, string startTimestamp = null)
        {
            DeviceEventRowList deviceMessages = new DeviceEventRowList(batchIndex, batchSize);
            IReliableDictionary <DateTimeOffset, DeviceEventSeries> storeCompletedMessages = storeCompletedMessages = await this.stateManager.GetOrAddAsync <IReliableDictionary <DateTimeOffset, DeviceEventSeries> >(TargetSolution.Names.EventHistoryDictionaryName);

            DateTimeOffset searchStartTimestamp           = DateTimeOffset.Parse("1970-01-01T00:00:00.000Z");
            bool           searchStartTimestampUpdateFlag = true;

            if (storeCompletedMessages != null)
            {
                long totalCount = await GetQueueLengthAsyncInternal();

                using (ITransaction tx = this.stateManager.CreateTransaction())
                {
                    if (totalCount > 0)
                    {
                        bool useIndexForTotalCount = false;
                        IAsyncEnumerable <KeyValuePair <DateTimeOffset, DeviceEventSeries> > enumerable = null;
                        IAsyncEnumerator <KeyValuePair <DateTimeOffset, DeviceEventSeries> > enumerator = null;

                        if (startTimestamp == null)
                        {
                            enumerable = await storeCompletedMessages.CreateEnumerableAsync(tx, EnumerationMode.Ordered);
                        }
                        else
                        {
                            searchStartTimestamp = DateTimeOffset.Parse(startTimestamp).ToUniversalTime();

                            enumerable = await storeCompletedMessages.CreateEnumerableAsync(tx, EnumerationMode.Ordered);

                            enumerator = enumerable.GetAsyncEnumerator();

                            await enumerator.MoveNextAsync(appLifetime.ApplicationStopping);

                            if (enumerator.Current.Key.CompareTo(searchStartTimestamp) < 0)
                            {
                                useIndexForTotalCount = true;
                                enumerable            = await storeCompletedMessages.CreateEnumerableAsync(tx, key => key.CompareTo(searchStartTimestamp) >= 0, EnumerationMode.Ordered);
                            }
                            else
                            {
                                enumerable = await storeCompletedMessages.CreateEnumerableAsync(tx, EnumerationMode.Ordered);
                            }
                        }

                        enumerator = enumerable.GetAsyncEnumerator();

                        int indexStart = batchIndex;

                        if (indexStart < 0)
                        {
                            indexStart = 0;
                        }
                        else if (indexStart > 0)
                        {
                            indexStart--;
                        }

                        indexStart = indexStart * batchSize;

                        int indexEnd = indexStart + batchSize;

                        if (!useIndexForTotalCount)
                        {
                            totalCount -= indexStart;
                        }

                        int index = 1;
                        while (await enumerator.MoveNextAsync(appLifetime.ApplicationStopping))
                        {
                            if (searchStartTimestampUpdateFlag)
                            {
                                searchStartTimestamp           = enumerator.Current.Key;
                                searchStartTimestampUpdateFlag = false;
                            }

                            if (deviceId == null || deviceId == enumerator.Current.Value.DeviceId)
                            {
                                if (index > indexStart && index <= indexEnd)
                                {
                                    foreach (DeviceEvent evnt in enumerator.Current.Value.Events)
                                    {
                                        deviceMessages.AddRow(new DeviceEventRow(enumerator.Current.Key, evnt.Timestamp, enumerator.Current.Value.DeviceId, evnt.MeasurementType, evnt.SensorIndex, evnt.TempExternal, evnt.TempInternal, evnt.BatteryLevel, evnt.DataPointsCount));
                                        break;
                                    }
                                }

                                if (index == indexEnd & !useIndexForTotalCount)
                                {
                                    break;
                                }
                                index++;
                            }
                        }

                        if (useIndexForTotalCount)
                        {
                            totalCount = (long)index;
                        }
                    }
                    await tx.CommitAsync();

                    deviceMessages.TotalCount           = totalCount;
                    deviceMessages.SearchStartTimestamp = searchStartTimestamp;
                    ServiceEventSource.Current.ServiceMessage(this.context, $"DataService - SearchDevicesHistoryByPage - Count of[{deviceMessages.TotalCount}] for data range from [{startTimestamp}] - device [{deviceId ?? "All"}]");
                }
            }

            return(this.Json(deviceMessages));
        }