Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is an IAsyncCursor used for iteration with the mongodb c# driver?

Tags:

I'm trying to get a list of all the databases in my server and ultimately print them out (i.e. use their names as strings). With the previous version of the c# driver I could call the Server.GetDatabases(), but that has been replaced with ListDatabasesAsync().

The return value is an IAsyncCursor<> and I'm not sure what to do with it. How does one iterate through the list of databases (or anything) with such a cursor?

like image 243
Shamster Avatar asked Apr 16 '15 17:04

Shamster


2 Answers

You have 3 options:

  1. Use the built-in driver method (e.g. ForEachAsync, ToListAsync).
  2. On C# 8.0 and above you can convert the IAsyncCursor into an IAsyncEnumerable and use await foreach or any async LINQ operator.
  3. Iterate over the IAsyncCursor.
Built-in Driver Methods

The driver has some LINQ-like extension methods for IAsyncCursor, like AnyAsync, ToListAsync, etc. For iteration it has ForEachAsync:

var cursor = await client.ListDatabasesAsync(); await cursor.ForEachAsync(db => Console.WriteLine(db["name"])); 
Converting to IAsyncEnumerable

On C# 8.0 and above it's much nicer to iterate with await foreach (and use async LINQ). This requires wrapping the IAsyncCursor in an IAsyncEnumerable. You can do it yourself but since its important to get some critical things right (like cancellation and disposal) I've published a nuget package: MongoAsyncEnumerableAdapter

var cursor = await client.ListDatabasesAsync(); await foreach (var db in cursor.ToAsyncEnumerable()) {     Console.WriteLine(db["name"]); } 
Custom iteration

Traditional iteration in C# is done with IEnumerable and foreach. foreach is the compiler's syntactic sugar. It's actually a call to GetEnumerator, a using scope and a while loop:

using (var enumerator = enumerable.GetEnumerator()) {     while (enumerator.MoveNext())     {         var current = enumerator.Current;         // use current.     } } 

IAsyncCursor is equivalent to IEnumerator (the result of IEnumerable.GetEnumerator) while IAsyncCursorSource is to IEnumerable. The difference is that these support async (and get a batch each iteration and not just a single item). So you can implement the whole using, while loop thing yourself:

IAsyncCursorSource<int> cursorSource = null;  using (var asyncCursor = await cursorSource.ToCursorAsync()) {     while (await asyncCursor.MoveNextAsync())     {         foreach (var current in asyncCursor.Current)         {             // use current         }     } } 
like image 158
i3arnon Avatar answered Sep 17 '22 07:09

i3arnon


I personally like to convert the cursor into a C# 8 IAsyncEnumerable, that way you get all the benefits of working with enumerables (LINQ mainly).

Using @i3arnon's "long answer" I created this extension method:

public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(this IAsyncCursor<T> asyncCursor) {     while (await asyncCursor.MoveNextAsync())     {         foreach (var current in asyncCursor.Current)         {             yield return current;         }     } } 
like image 33
Tom Gringauz Avatar answered Sep 18 '22 07:09

Tom Gringauz