Skip to content

Commit a17b093

Browse files
committed
address pr comments
1 parent 7b62225 commit a17b093

File tree

8 files changed

+329
-16
lines changed

8 files changed

+329
-16
lines changed

src/MongoDB.Driver/Core/IAsyncCursor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ public static class IAsyncCursorExtensions
368368
/// <returns>An IAsyncEnumerable.</returns>
369369
public static IAsyncEnumerable<TDocument> ToAsyncEnumerable<TDocument>(this IAsyncCursor<TDocument> cursor)
370370
{
371-
return new AsyncCursorEnumerableOneTimeAdapter<TDocument>(cursor);
371+
return new AsyncCursorEnumerableOneTimeAdapter<TDocument>(cursor, CancellationToken.None);
372372
}
373373

374374
/// <summary>

src/MongoDB.Driver/Core/IAsyncCursorSource.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,14 +337,15 @@ public static class IAsyncCursorSourceExtensions
337337
}
338338

339339
/// <summary>
340-
/// Wraps a cursor source in an IAsyncEnumerable. Each time GetAsyncEnumerator is called a new cursor is fetched from the cursor source.
340+
/// Wraps a cursor source in an IAsyncEnumerable. Each time GetAsyncEnumerator is called a new enumerator is returned and a new cursor
341+
/// is fetched from the cursor source on the first call to MoveNextAsync.
341342
/// </summary>
342343
/// <typeparam name="TDocument">The type of the document.</typeparam>
343344
/// <param name="source">The source.</param>
344345
/// <returns>An IAsyncEnumerable.</returns>
345346
public static IAsyncEnumerable<TDocument> ToAsyncEnumerable<TDocument>(this IAsyncCursorSource<TDocument> source)
346347
{
347-
return new AsyncCursorSourceEnumerableAdapter<TDocument>(source);
348+
return new AsyncCursorSourceEnumerableAdapter<TDocument>(source, CancellationToken.None);
348349
}
349350

350351
/// <summary>

src/MongoDB.Driver/Core/Operations/AsyncCursorEnumerableOneTimeAdapter.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ internal sealed class AsyncCursorEnumerableOneTimeAdapter<TDocument> : IEnumerab
2727
private readonly IAsyncCursor<TDocument> _cursor;
2828
private bool _hasBeenEnumerated;
2929

30-
public AsyncCursorEnumerableOneTimeAdapter(IAsyncCursor<TDocument> cursor)
31-
: this(cursor, CancellationToken.None)
32-
{
33-
}
34-
3530
public AsyncCursorEnumerableOneTimeAdapter(IAsyncCursor<TDocument> cursor, CancellationToken cancellationToken)
3631
{
3732
_cursor = Ensure.IsNotNull(cursor, nameof(cursor));

src/MongoDB.Driver/Core/Operations/AsyncCursorEnumerator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ public void Dispose()
7575

7676
public ValueTask DisposeAsync()
7777
{
78+
// TODO: implement true async disposal (CSHARP-5630)
7879
Dispose();
79-
return default;
80+
81+
// TODO: convert to ValueTask.CompletedTask once we stop supporting older target frameworks
82+
return default; // Equivalent to ValueTask.CompletedTask which is not available on older target frameworks.
8083
}
8184

8285
public bool MoveNext()

src/MongoDB.Driver/Core/Operations/AsyncCursorSourceEnumerableAdapter.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ internal class AsyncCursorSourceEnumerableAdapter<TDocument> : IEnumerable<TDocu
2727
private readonly IAsyncCursorSource<TDocument> _source;
2828

2929
// constructors
30-
public AsyncCursorSourceEnumerableAdapter(IAsyncCursorSource<TDocument> source)
31-
: this(source, CancellationToken.None)
32-
{
33-
}
34-
3530
public AsyncCursorSourceEnumerableAdapter(IAsyncCursorSource<TDocument> source, CancellationToken cancellationToken)
3631
{
3732
_source = Ensure.IsNotNull(source, nameof(source));
@@ -40,8 +35,7 @@ public AsyncCursorSourceEnumerableAdapter(IAsyncCursorSource<TDocument> source,
4035

4136
public IAsyncEnumerator<TDocument> GetAsyncEnumerator(CancellationToken cancellationToken = default)
4237
{
43-
var cursor = _source.ToCursor(cancellationToken);
44-
return new AsyncCursorEnumerator<TDocument>(cursor, cancellationToken);
38+
return new AsyncCursorSourceEnumerator<TDocument>(_source, cancellationToken);
4539
}
4640

4741
// public methods
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.Threading;
19+
using System.Threading.Tasks;
20+
using MongoDB.Driver.Core.Misc;
21+
22+
namespace MongoDB.Driver.Core.Operations
23+
{
24+
#pragma warning disable CA1001
25+
internal class AsyncCursorSourceEnumerator<TDocument> : IAsyncEnumerator<TDocument>
26+
#pragma warning restore CA1001
27+
{
28+
private readonly CancellationToken _cancellationToken;
29+
private AsyncCursorEnumerator<TDocument> _cursorEnumerator;
30+
private readonly IAsyncCursorSource<TDocument> _cursorSource;
31+
private bool _disposed;
32+
33+
public AsyncCursorSourceEnumerator(IAsyncCursorSource<TDocument> cursorSource, CancellationToken cancellationToken)
34+
{
35+
_cursorSource = Ensure.IsNotNull(cursorSource, nameof(cursorSource));
36+
_cancellationToken = cancellationToken;
37+
}
38+
39+
public TDocument Current
40+
{
41+
get
42+
{
43+
if (_cursorEnumerator == null)
44+
{
45+
throw new InvalidOperationException("Enumeration has not started. Call MoveNextAsync.");
46+
}
47+
return _cursorEnumerator.Current;
48+
}
49+
}
50+
51+
public async ValueTask DisposeAsync()
52+
{
53+
if (!_disposed)
54+
{
55+
_disposed = true;
56+
57+
if (_cursorEnumerator != null)
58+
{
59+
await _cursorEnumerator.DisposeAsync().ConfigureAwait(false);
60+
}
61+
62+
GC.SuppressFinalize(this);
63+
}
64+
}
65+
66+
public async ValueTask<bool> MoveNextAsync()
67+
{
68+
ThrowIfDisposed();
69+
70+
if (_cursorEnumerator == null)
71+
{
72+
var cursor = await _cursorSource.ToCursorAsync(_cancellationToken).ConfigureAwait(false);
73+
_cursorEnumerator = new AsyncCursorEnumerator<TDocument>(cursor, _cancellationToken);
74+
}
75+
76+
return await _cursorEnumerator.MoveNextAsync().ConfigureAwait(false);
77+
}
78+
79+
public void Reset()
80+
{
81+
ThrowIfDisposed();
82+
throw new NotSupportedException();
83+
}
84+
85+
// private methods
86+
private void ThrowIfDisposed()
87+
{
88+
if (_disposed)
89+
{
90+
throw new ObjectDisposedException(GetType().Name);
91+
}
92+
}
93+
}
94+
}

tests/MongoDB.Driver.Tests/Core/Operations/AsyncCursorEnumerableOneTimeAdapterTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using System.Threading;
18+
using System.Threading.Tasks;
1819
using FluentAssertions;
1920
using MongoDB.Bson;
2021
using Moq;
@@ -32,6 +33,37 @@ public void constructor_should_throw_when_cursor_is_null()
3233
action.ShouldThrow<ArgumentNullException>().And.ParamName.Should().Be("cursor");
3334
}
3435

36+
[Fact]
37+
public async Task GetAsyncEnumerator_should_return_expected_result()
38+
{
39+
var mockCursor = new Mock<IAsyncCursor<BsonDocument>>();
40+
mockCursor.SetupSequence(c => c.MoveNextAsync(CancellationToken.None)).ReturnsAsync(true).ReturnsAsync(false);
41+
mockCursor.Setup(c => c.Current).Returns(new[] { new BsonDocument("_id", 0) });
42+
var subject = new AsyncCursorEnumerableOneTimeAdapter<BsonDocument>(mockCursor.Object, CancellationToken.None);
43+
44+
var result = subject.GetAsyncEnumerator();
45+
46+
var result1 = await result.MoveNextAsync();
47+
result1.Should().BeTrue();
48+
49+
result.Current.Should().Be(new BsonDocument("_id", 0));
50+
51+
var result2 = await result.MoveNextAsync();
52+
result2.Should().BeFalse();
53+
}
54+
55+
[Fact]
56+
public void GetAsyncEnumerator_should_throw_when_called_more_than_once()
57+
{
58+
var mockCursor = new Mock<IAsyncCursor<BsonDocument>>();
59+
var subject = new AsyncCursorEnumerableOneTimeAdapter<BsonDocument>(mockCursor.Object, CancellationToken.None);
60+
subject.GetAsyncEnumerator();
61+
62+
Action action = () => subject.GetAsyncEnumerator();
63+
64+
action.ShouldThrow<InvalidOperationException>();
65+
}
66+
3567
[Fact]
3668
public void GetEnumerator_should_return_expected_result()
3769
{

0 commit comments

Comments
 (0)