diff --git a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs index 6f26855d2be..d51a2686eff 100644 --- a/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs +++ b/src/MongoDB.Driver/Linq/IMongoQueryProvider.cs @@ -40,6 +40,15 @@ public interface IMongoQueryProvider : IQueryProvider /// The cancellation token. /// The value that results from executing the specified query. Task ExecuteAsync(Expression expression, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Translates an Expression to MQL. + /// + /// The expression. + /// The output serializer. + /// The type of the result. + /// An array of MQL pipeline stages represented as BsonDocuments. + BsonDocument[] Translate(Expression expression, out IBsonSerializer outputSerializer); } /// diff --git a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs index 2717a7e71d7..e411b2ac0c5 100644 --- a/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs +++ b/src/MongoDB.Driver/Linq/Linq3Implementation/MongoQueryProvider.cs @@ -55,6 +55,7 @@ protected MongoQueryProvider( public abstract TResult Execute(Expression expression); public abstract Task ExecuteAsync(Expression expression, CancellationToken cancellationToken); public abstract ExpressionTranslationOptions GetTranslationOptions(); + public abstract BsonDocument[] Translate(Expression expression, out IBsonSerializer outputSerializer); } internal sealed class MongoQueryProvider : MongoQueryProvider @@ -152,5 +153,14 @@ public override ExpressionTranslationOptions GetTranslationOptions() var database = _database ?? _collection?.Database; return translationOptions.AddMissingOptionsFrom(database?.Client.Settings.TranslationOptions); } + + public override BsonDocument[] Translate(Expression expression, out IBsonSerializer outputSerializer) + { + var translationOptions = GetTranslationOptions(); + var executableQuery = ExpressionToExecutableQueryTranslator.Translate(provider: this, expression, translationOptions); + var stages = executableQuery.Pipeline.Ast.Stages; + outputSerializer = (IBsonSerializer)executableQuery.Pipeline.OutputSerializer; + return stages.Select(s => s.Render().AsBsonDocument).ToArray(); + } } } diff --git a/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5473Tests.cs b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5473Tests.cs new file mode 100644 index 00000000000..8cf20decca5 --- /dev/null +++ b/tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Jira/CSharp5473Tests.cs @@ -0,0 +1,57 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq; +using FluentAssertions; +using MongoDB.Driver.Linq; +using Xunit; + +namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira +{ + public class CSharp5473Tests : Linq3IntegrationTest + { + [Fact] + public void Translate_expression_should_work() + { + var collection = GetCollection(); + var queryable = collection.AsQueryable() + .Select(x => x.X + 1); + var expression = queryable.Expression; // queryable was just used as an easy way to create the expression and the provider + var provider = (IMongoQueryProvider)queryable.Provider; + + var stages = provider.Translate(expression, out var outputSerializer); + AssertStages(stages, "{ $project : { _v : { $add : ['$X', 1] }, _id : 0 } }"); + + var pipeline = new BsonDocumentStagePipelineDefinition(stages, outputSerializer); + var result = collection.Aggregate(pipeline).Single(); + result.Should().Be(2); + } + + private IMongoCollection GetCollection() + { + var collection = GetCollection("test"); + CreateCollection( + collection, + new C { Id = 1, X = 1 }); + return collection; + } + + private class C + { + public int Id { get; set; } + public int X { get; set; } + } + } +}