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); } } } }
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); } } } }
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); }