/// <summary> /// Query the reliable collection with the given name from the reliable state manager using the given query parameters. /// </summary> /// <param name="stateManager">Reliable state manager for the replica.</param> /// <param name="context">Stateful Service Context.</param> /// <param name="collection">Name of the reliable collection.</param> /// <param name="query">OData query parameters.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The json serialized results of the query.</returns> public static async Task <IEnumerable <JToken> > QueryAsync(this IReliableStateManager stateManager, StatefulServiceContext context, string collection, IEnumerable <KeyValuePair <string, string> > query, CancellationToken cancellationToken) { // Get the list of partitions (excluding the executing partition). var partitions = await StatefulServiceUtils.GetPartitionsAsync(context).ConfigureAwait(false); // Query all service partitions concurrently. var remoteQueries = partitions.Select(p => QueryPartitionAsync(p, context, collection, query, cancellationToken)); var localQuery = stateManager.QueryPartitionAsync(collection, query, context.PartitionId, cancellationToken); var queries = remoteQueries.Concat(new[] { localQuery }); // Aggregate all query results into a single list. var queryResults = await Task.WhenAll(queries).ConfigureAwait(false); var results = queryResults.SelectMany(r => r); // Run the aggregation query to get the final results (e.g. for top, orderby, project). var reliableState = await stateManager.GetQueryableState(collection).ConfigureAwait(false); var entityType = reliableState.GetEntityType(); var objects = results.Select(r => r.ToObject(entityType)); var queryResult = ApplyQuery(objects, entityType, query, aggregate: true); results = queryResult.Select(q => JObject.FromObject(q)); // Return the filtered data as json. return(results); }
/// <summary> /// This implementation should not leak into the core Queryable code. It is dependent on the specific protocol /// used to communicate with the other partitions (HTTP over ReverseProxy), and should be hidden behind an interface. /// </summary> private static async Task <IEnumerable <JToken> > QueryPartitionAsync(Partition partition, StatefulServiceContext context, string collection, IEnumerable <KeyValuePair <string, string> > query, CancellationToken cancellationToken) { string endpoint = await StatefulServiceUtils.GetPartitionEndpointAsync(context, partition).ConfigureAwait(false); string content = await StatefulServiceUtils.QueryPartitionAsync(endpoint, partition.PartitionInformation.Id, collection, query).ConfigureAwait(false); var result = JsonConvert.DeserializeObject <ODataResult>(content); return(result.Value); }
private async Task ForwardQueryCollectionAsync(HttpContext httpContext, StatefulServiceContext serviceContext, IReliableStateManager stateManager, string collection, Guid partitionId) { // Forward the request to the correct partition. var queryParameters = httpContext.Request.Query.Select(q => new KeyValuePair <string, string>(q.Key, q.Value)); string endpoint = await StatefulServiceUtils.GetPartitionEndpointAsync(serviceContext, partitionId).ConfigureAwait(false); string content = await StatefulServiceUtils.QueryPartitionAsync(endpoint, partitionId, collection, queryParameters); httpContext.Response.ContentType = "application/json"; httpContext.Response.StatusCode = (int)HttpStatusCode.OK; // Write the response. await httpContext.Response.WriteAsync(content).ConfigureAwait(false); }