public static string GetQuery <TEntity>(AggregateExecutionModel model)
        {
            var entityDefinition = EntityMapping.GetOrCreateDefinition(typeof(TEntity));
            var stages           = string.Join(", ", model.Stages.Select(x => x.ToString()));

            return($"db.{entityDefinition.CollectionName}.aggregate([{stages}])");
        }
        private AggregateExecutionModel GetExecutionModel(Expression expression, bool isAsync = false)
        {
            //Use the official driver to do the heavy lifting on the query translation
            var underlyingProvider = GetCollection().AsQueryable().Provider;
            var providerType       = underlyingProvider.GetType();                      //Type: MongoQueryProviderImpl (internal)
            var translatedQuery    = providerType.GetMethod("Translate", BindingFlags.NonPublic | BindingFlags.Instance)
                                     .Invoke(underlyingProvider, new[] { expression }); //Type: QueryableTranslation (internal)
            var translatedQueryType = translatedQuery.GetType();

            //We can't cast to AggregateQueryableExecutionModel<T> directly as we don't have generic parameter T
            //While it may be TEntity, it could also be something else
            var underlyingExecutionModel = translatedQueryType.GetProperty("Model").GetValue(translatedQuery) as QueryableExecutionModel;
            var modelType = underlyingExecutionModel.GetType();             //Assumed type: AggregateQueryableExecutionModel<T>

            //Retrieve the stages from reflection
            var expressionStages = modelType.GetProperty(nameof(AggregateQueryableExecutionModel <object> .Stages))
                                   .GetValue(underlyingExecutionModel) as IEnumerable <BsonDocument>;

            //Retreve the serializer from reflection
            var serializer = modelType.GetProperty(nameof(AggregateQueryableExecutionModel <object> .OutputSerializer))
                             .GetValue(underlyingExecutionModel) as IBsonSerializer;

            if (PreStage != null)
            {
                expressionStages = new[] { PreStage }.Concat(expressionStages);
            }

            var result = new AggregateExecutionModel
            {
                Stages     = expressionStages,
                Serializer = serializer
            };

            //Get the result transforming lambda (allows things like FirstOrDefault, Count, Average etc to work properly)
            var resultTransformer = translatedQueryType.GetProperty("ResultTransformer").GetValue(translatedQuery);             //Type: Mixed (implements IResultTransformer (internal))

            if (resultTransformer != null)
            {
                result.ResultTransformer = ResultTransformers.Transform(expression, serializer.ValueType, isAsync) as LambdaExpression;

                //Note: In the future this can change from the initial reflection to a `TryTransform` function where it checks the expression itself
                //		The reason we are doing this method first is to weed out the bugs and any core missing functionality.
            }

            return(result);
        }
        private async IAsyncEnumerable <TResult> ExecuteModelAsync <TResult>(AggregateExecutionModel model, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            var serializer = model.Serializer as IBsonSerializer <TResult>;
            var pipeline   = PipelineDefinition <TEntity, TResult> .Create(model.Stages, serializer);

            using (var diagnostics = DiagnosticRunner.Start <TEntity>(Connection, model))
            {
                IAsyncCursor <TResult> underlyingCursor;

                try
                {
                    underlyingCursor = await GetCollection().AggregateAsync(pipeline, cancellationToken: cancellationToken);
                }
                catch (Exception exception)
                {
                    diagnostics.Error(exception);
                    throw;
                }

                var hasFirstResult = false;
                while (await underlyingCursor.MoveNextAsync(cancellationToken))
                {
                    if (!hasFirstResult)
                    {
                        hasFirstResult = true;
                        diagnostics.FirstReadResult <TResult>();
                    }

                    var resultBatch = underlyingCursor.Current;
                    foreach (var item in resultBatch)
                    {
                        if (item is TEntity entityItem &&
                            (model.ResultTransformer == null ||
                             model.ResultTransformer.ReturnType == typeof(ValueTask <TEntity>) ||
                             model.ResultTransformer.ReturnType == typeof(Task <TEntity>)))
                        {
                            EntityProcessors.ProcessEntity(entityItem, Connection);
                        }

                        yield return(item);
                    }
                }
            }
        }
Beispiel #4
0
        private IEnumerable <TResult> ExecuteModel <TResult>(AggregateExecutionModel model)
        {
            var serializer = model.Serializer as IBsonSerializer <TResult>;
            var pipeline   = PipelineDefinition <TEntity, TResult> .Create(model.Stages, serializer);

            using (var diagnostics = DiagnosticRunner.Start <TEntity>(Connection, model))
            {
                IEnumerable <TResult> underlyingResult;

                try
                {
                    underlyingResult = GetCollection().Aggregate(pipeline).ToEnumerable();
                }
                catch (Exception exception)
                {
                    diagnostics.Error(exception);
                    throw;
                }

                using (var enumerator = underlyingResult.GetEnumerator())
                {
                    var hasFirstResult = false;
                    while (enumerator.MoveNext())
                    {
                        if (!hasFirstResult)
                        {
                            hasFirstResult = true;
                            diagnostics.FirstReadResult <TResult>();
                        }

                        var item = enumerator.Current;
                        if (item is TEntity entityItem)
                        {
                            EntityProcessors.ProcessEntity(entityItem, Connection);
                        }
                        yield return(item);
                    }
                }
            }
        }
Beispiel #5
0
        private AggregateExecutionModel GetExecutionModel(Expression expression)
        {
            var underlyingProvider = GetCollection().AsQueryable().Provider;
            var providerType       = underlyingProvider.GetType();

            var translatedQuery = providerType.GetMethod("Translate", BindingFlags.NonPublic | BindingFlags.Instance)
                                  .Invoke(underlyingProvider, new[] { expression });
            var translatedQueryType = translatedQuery.GetType();

            //Get the model stages and processing type
            var underlyingExecutionModel = translatedQueryType.GetProperty("Model").GetValue(translatedQuery) as QueryableExecutionModel;
            var modelType        = underlyingExecutionModel.GetType();      //Assumed type: AggregateQueryableExecutionModel<>
            var expressionStages = modelType.GetProperty(nameof(AggregateQueryableExecutionModel <object> .Stages))
                                   .GetValue(underlyingExecutionModel) as IEnumerable <BsonDocument>;
            var serializer = modelType.GetProperty(nameof(AggregateQueryableExecutionModel <object> .OutputSerializer))
                             .GetValue(underlyingExecutionModel) as IBsonSerializer;

            if (PreStage != null)
            {
                expressionStages = new[] { PreStage }.Concat(expressionStages);
            }

            var result = new AggregateExecutionModel
            {
                Stages     = expressionStages,
                Serializer = serializer
            };

            //Get the result transforming lambda (allows things like FirstOrDefault, Count, Average etc to work properly)
            var resultTransformer = translatedQueryType.GetProperty("ResultTransformer").GetValue(translatedQuery);

            if (resultTransformer != null)
            {
                var resultTransformerType = resultTransformer.GetType();
                var lambda = resultTransformerType.GetMethod("CreateAggregator").Invoke(resultTransformer, new[] { serializer.ValueType });
                result.ResultTransformer = lambda as LambdaExpression;
            }

            return(result);
        }