/// <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="serviceContext">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 serviceContext, HttpContext httpContext, string collection, IEnumerable <KeyValuePair <string, string> > query, bool isStandaloneReplica, CancellationToken cancellationToken) { // Get the list of partitions (excluding the executing partition). var partitions = Enumerable.Empty <Partition>(); if (!isStandaloneReplica) { partitions = await StatefulServiceUtils.GetPartitionsAsync(serviceContext).ConfigureAwait(false); } // Query all service partitions concurrently. var remoteQueries = partitions.Select(p => QueryPartitionAsync(p, serviceContext, collection, query, cancellationToken)); var localQuery = stateManager.QueryPartitionAsync(httpContext, collection, query, serviceContext.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(httpContext, 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> /// 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="reliableStateName">Name of the reliable state to be queried.</param> /// <returns>The json serialized results of the query.</returns> public static async Task <List <string> > QueryAsync(this IReliableStateManager stateManager, string reliableStateName) { using (var tx = stateManager.CreateTransaction()) { IReliableState reliableState = await stateManager.GetQueryableState(reliableStateName).ConfigureAwait(false); // Get the data from the reliable state var results = await reliableState.GetAsyncEnumerable(tx, stateManager).ConfigureAwait(false); try { // Convert to json var json = await results.SelectAsync(r => r.ToString()).AsEnumerable().ConfigureAwait(false); await tx.CommitAsync().ConfigureAwait(false); // Return the data as json string return(json.ToList()); } catch (Exception e) { throw e; } } }
/// <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="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 <string> > QueryPartitionAsync(this IReliableStateManager stateManager, string collection, IEnumerable <KeyValuePair <string, string> > query, CancellationToken cancellationToken) { // Find the reliable state. var reliableState = await stateManager.GetQueryableState(collection).ConfigureAwait(false); // Get the data from the reliable state. var results = await reliableState.GetEnumerable(stateManager, cancellationToken).ConfigureAwait(false); // Filter the data. if (query.Any()) { var valueType = reliableState.GetValueType(); results = ApplyQuery(results, valueType, query, aggregate: false); } // Return the filtered data as json. return(results.Select(JsonConvert.SerializeObject)); }
/// <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="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 <string> > QueryAsync(this IReliableStateManager stateManager, StatefulServiceContext context, string collection, IEnumerable <KeyValuePair <string, string> > query, CancellationToken cancellationToken) { // Query all service partitions concurrently. var proxies = await GetServiceProxiesAsync <IQueryableService>(context).ConfigureAwait(false); var queries = proxies.Select(p => p.QueryPartitionAsync(collection, query)).Concat(new[] { stateManager.QueryPartitionAsync(collection, query, cancellationToken) }); 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). if (query.Any()) { var reliableState = await stateManager.GetQueryableState(collection).ConfigureAwait(false); var valueType = reliableState.GetValueType(); var objects = results.Select(r => JsonConvert.DeserializeObject(r, valueType)); var queryResult = ApplyQuery(objects, valueType, query, aggregate: true); results = queryResult.Select(JsonConvert.SerializeObject); } // Return the filtered data as json. return(results); }
/// <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="collection">Name of the reliable collection.</param> /// <param name="query">OData query parameters.</param> /// <param name="partitionId">Partition id.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The json serialized results of the query.</returns> public static async Task <IEnumerable <JToken> > QueryPartitionAsync(this IReliableStateManager stateManager, string collection, IEnumerable <KeyValuePair <string, string> > query, Guid partitionId, CancellationToken cancellationToken) { // Find the reliable state. var reliableState = await stateManager.GetQueryableState(collection).ConfigureAwait(false); using (var tx = stateManager.CreateTransaction()) { // Get the data from the reliable state. var results = await reliableState.GetAsyncEnumerable(tx, stateManager, partitionId, cancellationToken).ConfigureAwait(false); // Filter the data. var entityType = reliableState.GetEntityType(); results = ApplyQuery(results, entityType, query, aggregate: false); // Convert to json. var json = await results.SelectAsync(r => JObject.FromObject(r)).AsEnumerable().ConfigureAwait(false); await tx.CommitAsync().ConfigureAwait(false); // Return the filtered data as json. return(json); } }
/// <summary> /// Execute the operations given in <paramref name="operations"/> in a transaction. /// </summary> /// <param name="stateManager">Reliable state manager for the replica.</param> /// <param name="operations">Operations (add/update/delete) to perform against collections in the partition.</param> /// <returns>A list of status codes indicating success/failure of the operations.</returns> public static async Task <List <EntityOperationResult> > ExecuteAsync(this IReliableStateManager stateManager, HttpContext httpContext, EntityOperation <JToken, JToken>[] operations) { var results = new List <EntityOperationResult>(); using (var tx = stateManager.CreateTransaction()) { bool commit = true; foreach (var operation in operations) { HttpStatusCode status = HttpStatusCode.BadRequest; string description = null; try { // Get the reliable dictionary for this operation. var dictionary = await stateManager.GetQueryableState(httpContext, operation.Collection).ConfigureAwait(false); // Execute operation. if (operation.Operation == Operation.Add) { status = await ExecuteAddAsync(tx, dictionary, operation).ConfigureAwait(false); } else if (operation.Operation == Operation.Update) { status = await ExecuteUpdateAsync(tx, dictionary, operation).ConfigureAwait(false); } else if (operation.Operation == Operation.Delete) { status = await ExecuteDeleteAsync(tx, dictionary, operation).ConfigureAwait(false); } } catch (QueryException e) { status = e.Status; description = e.Message; } catch (ArgumentException e) { status = HttpStatusCode.BadRequest; description = e.Message; } catch (Exception) { status = HttpStatusCode.InternalServerError; } // Add the operation result. results.Add(new EntityOperationResult { PartitionId = operation.PartitionId, Collection = operation.Collection, Key = operation.Key, Status = (int)status, Description = description, }); // If any operation failed, abort the transaction. if (!status.IsSuccessStatusCode()) { commit = false; } } // Commit or abort the transaction. if (commit) { await tx.CommitAsync().ConfigureAwait(false); } else { tx.Abort(); } } return(results); }
/// <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="collection">Name of the reliable collection.</param> /// <param name="query">OData query parameters.</param> /// <param name="partitionId">Partition id.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The json serialized results of the query.</returns> public static async Task <IEnumerable <JToken> > QueryPartitionAsync(this IReliableStateManager stateManager, HttpContext httpContext, string collection, IEnumerable <KeyValuePair <string, string> > query, Guid partitionId, CancellationToken cancellationToken) { // Find the reliable state (boilerplate) IReliableState reliableState = await stateManager.GetQueryableState(httpContext, collection).ConfigureAwait(false); var entityType = reliableState.GetEntityType(); // Type Information about the dictionary // Types of the objects in the dictionary, used by rest of types Type dictionaryKeyType = entityType.GenericTypeArguments[0]; Type dictionaryValueType = entityType.GenericTypeArguments[1]; // Generate an ODataQuery Object ODataQueryContext queryContext = QueryCache.GetQueryContext(entityType); ODataQueryOptions queryOptions = new ODataQueryOptions(query, queryContext, aggregate: false); MethodInfo tryFilterQuery = typeof(ReliableStateExtensions).GetMethod("TryFilterQuery", BindingFlags.NonPublic | BindingFlags.Static); tryFilterQuery = tryFilterQuery.MakeGenericMethod(new Type[] { dictionaryKeyType, dictionaryValueType }); Task filteredKVTask = (Task)tryFilterQuery.Invoke(null, new object[] { queryOptions, stateManager, collection, cancellationToken }); await filteredKVTask; dynamic filteredKV = filteredKVTask.GetType().GetProperty("Result").GetValue(filteredKVTask); // It was not filterable, have to use AsyncEnumerable over the original dictionary if (filteredKV == null) { // If the query does not contain a valid $filter clause or there is no secondary index for that $filter, does an entire dictionary lookup using (var tx = stateManager.CreateTransaction()) { // Get the data from the reliable state var results = await reliableState.GetAsyncEnumerable(tx, stateManager, partitionId, cancellationToken).ConfigureAwait(false); // Filter the data // var entityType = reliableState.GetEntityType(); results = ApplyQuery(results, entityType, query, aggregate: false); // Convert to json var json = await results.SelectAsync(r => JObject.FromObject(r)).AsEnumerable().ConfigureAwait(false); await tx.CommitAsync().ConfigureAwait(false); // Return the filtered data as json return(json); } } else { // Turns List to IEnumerable, thens turn to IAsyncEnumerable, then turns to IAsyncEnumerable<Entity> which is what Query infra wants MethodInfo asAsyncEnumerableMethod = typeof(AsyncEnumerable).GetMethod("AsAsyncEnumerable").MakeGenericMethod(typeof(KeyValuePair <,>).MakeGenericType(new Type[] { dictionaryKeyType, dictionaryValueType }));; filteredKV = asAsyncEnumerableMethod.Invoke(null, new object[] { filteredKV }); MethodInfo asEntityMethod = typeof(ReliableStateExtensions).GetMethod("AsEntity", BindingFlags.NonPublic | BindingFlags.Static); asEntityMethod = asEntityMethod.MakeGenericMethod(new Type[] { dictionaryKeyType, dictionaryValueType }); filteredKV = asEntityMethod.Invoke(null, new object[] { filteredKV, partitionId, cancellationToken }); // Aplies the query to the results dynamic queryResults = ApplyQuery(filteredKV, entityType, query, aggregate: false); // Creates a lambda that will turn each element in results to a json object ParameterExpression objectParameterExpression = Expression.Parameter(typeof(object), "r"); //dictvaluetype is true type of object Expression jobjectExpression = Expression.Call(typeof(JObject).GetMethod("FromObject", new Type[] { typeof(object) }), objectParameterExpression); LambdaExpression jsonLambda = Expression.Lambda(jobjectExpression, new ParameterExpression[] { objectParameterExpression }); // Converts to json MethodInfo selectAsync = typeof(AsyncEnumerable).GetMethod("SelectAsync").MakeGenericMethod(new Type[] { typeof(object), typeof(JObject) }); dynamic jsonAsyncEnumerable = selectAsync.Invoke(null, new object[] { queryResults, (Func <object, JObject>)jsonLambda.Compile() }); // Converts json return to Enumerable of json MethodInfo asyncEnumerableAsEnumerableMethod = typeof(AsyncEnumerable).GetMethod("AsEnumerable").MakeGenericMethod(typeof(JObject)); Task jsonTask = (Task)asyncEnumerableAsEnumerableMethod.Invoke(null, new object[] { jsonAsyncEnumerable, default(CancellationToken) }); await jsonTask; IEnumerable <JObject> json = (IEnumerable <JObject>)jsonTask.GetType().GetProperty("Result").GetValue(jsonTask); // Return the filtered data as json return(json); } }