Skip to content

Commit d0c5aad

Browse files
committed
CSHARP-1449: LINQ now executes over the synchronous stack.
1 parent ed5c0b3 commit d0c5aad

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

src/MongoDB.Driver.Tests/Linq/MongoQueryableTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,27 @@ public async Task FirstOrDefaultAsync_with_predicate()
233233
result.Should().Be(11);
234234
}
235235

236+
[Test]
237+
public void Enumerable_foreach()
238+
{
239+
var query = from x in CreateQuery()
240+
select x.M;
241+
242+
int sum = 0;
243+
244+
foreach (var item in query)
245+
{
246+
sum += item.Sum();
247+
}
248+
249+
foreach (var item in query)
250+
{
251+
sum += item.Sum();
252+
}
253+
254+
sum.Should().Be(50);
255+
}
256+
236257
[Test]
237258
public void GroupBy_method()
238259
{

src/MongoDB.Driver/Linq/AggregateQueryableExecutionModel.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,25 @@ public override string ToString()
8282
return sb.ToString();
8383
}
8484

85+
internal override object Execute<TInput>(IMongoCollection<TInput> collection, AggregateOptions options)
86+
{
87+
var pipeline = CreatePipeline<TInput>();
88+
89+
return collection.Aggregate(pipeline, options, CancellationToken.None);
90+
}
91+
8592
internal override Task ExecuteAsync<TInput>(IMongoCollection<TInput> collection, AggregateOptions options, CancellationToken cancellationToken)
8693
{
87-
var pipeline = new BsonDocumentStagePipelineDefinition<TInput, TOutput>(
88-
_stages,
89-
_outputSerializer);
94+
var pipeline = CreatePipeline<TInput>();
9095

9196
return collection.AggregateAsync(pipeline, options, cancellationToken);
9297
}
9398

94-
internal override object Execute<TInput>(IMongoCollection<TInput> collection, AggregateOptions options)
99+
private BsonDocumentStagePipelineDefinition<TInput, TOutput> CreatePipeline<TInput>()
95100
{
96-
// TODO: modify LINQ to have a sync version of this
97-
return new AsyncCursorEnumerableAdapter<TOutput>(ct => ((Task<IAsyncCursor<TOutput>>)ExecuteAsync(collection, options, ct)).GetAwaiter().GetResult(), CancellationToken.None);
101+
return new BsonDocumentStagePipelineDefinition<TInput, TOutput>(
102+
_stages,
103+
_outputSerializer);
98104
}
99105
}
100106
}

src/MongoDB.Driver/Linq/ExecutionPlanBuilder.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using System.Linq.Expressions;
18+
using System.Threading;
1819

1920
namespace MongoDB.Driver.Linq
2021
{
@@ -28,12 +29,24 @@ public static Expression BuildPlan(Expression provider, QueryableTranslation tra
2829
null,
2930
Expression.Constant(translation.Model, typeof(QueryableExecutionModel)));
3031

32+
executor = Expression.Convert(
33+
executor,
34+
typeof(IAsyncCursor<>).MakeGenericType(translation.Model.OutputType));
35+
36+
// we have an IAsyncCursor at this point... need to change it into an IEnumerable
37+
executor = Expression.Call(
38+
typeof(IAsyncCursorExtensions),
39+
nameof(IAsyncCursorExtensions.ToEnumerable),
40+
new Type[] { translation.Model.OutputType },
41+
executor,
42+
Expression.Constant(CancellationToken.None));
43+
3144
if (translation.ResultTransformer != null)
3245
{
3346
var lambda = translation.ResultTransformer.CreateAggregator(translation.Model.OutputType);
3447
executor = Expression.Invoke(
3548
lambda,
36-
Expression.Convert(executor, lambda.Parameters[0].Type));
49+
executor);
3750
}
3851

3952
return executor;

0 commit comments

Comments
 (0)