/// <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);
        }
Beispiel #2
0
        /// <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);
            }
        }