IAsyncCursor 如何用于 mongodb c# 驱动程序的迭代?

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

我正在尝试获取服务器中所有数据库的列表并最终将它们打印出来(即使用它们的名称作为 strings)。使用以前版本的 c# 驱动程序,我可以调用 Server.GetDatabases(),但已替换为 ListDatabasesAsync()

return 值是一个 IAsyncCursor<>,我不确定如何处理它。如何使用这样的游标遍历数据库(或任何东西)列表?

您有 3 个选项:

  1. 使用内置驱动方法(例如ForEachAsyncToListAsync)。
  2. 在 C# 8.0 及更高版本上,您可以将 IAsyncCursor 转换为 IAsyncEnumerable 并使用 await foreach 或任何异步 LINQ 运算符。
  3. 遍历 IAsyncCursor
内置驱动方法

驱动程序有一些类似 LINQ 的扩展方法 IAsyncCursor,如 AnyAsyncToListAsync 等。对于迭代,它有 ForEachAsync:

var cursor = await client.ListDatabasesAsync();
await cursor.ForEachAsync(db => Console.WriteLine(db["name"]));
正在转换为 IAsyncEnumerable

在 C# 8.0 及更高版本上,使用 await foreach 进行迭代(并使用异步 LINQ)要好得多。这需要将 IAsyncCursor 包装在 IAsyncEnumerable 中。 你可以自己做,但由于正确处理一些关键的事情(比如取消和处置)很重要,我发布了一个 nuget 包:MongoAsyncEnumerableAdapter

var cursor = await client.ListDatabasesAsync();
await foreach (var db in cursor.ToAsyncEnumerable())
{
    Console.WriteLine(db["name"]);
}
自定义迭代

C# 中的传统迭代是使用 IEnumerableforeach 完成的。 foreach 是编译器的语法糖。它实际上是对 GetEnumeratorusing 作用域和 while 循环的调用:

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

IAsyncCursor 等价于 IEnumeratorIEnumerable.GetEnumerator 的结果),而 IAsyncCursorSource 等价于 IEnumerable。不同之处在于这些支持 async(并且每次迭代都获得一批,而不仅仅是单个项目)。所以你可以自己实现整个 using, while 循环:

IAsyncCursorSource<int> cursorSource = null;

using (var asyncCursor = await cursorSource.ToCursorAsync())
{
    while (await asyncCursor.MoveNextAsync())
    {
        foreach (var current in asyncCursor.Current)
        {
            // use current
        }
    }
}

我个人喜欢将游标转换为 C# 8 IAsyncEnumerable,这样您就可以获得使用枚举的所有好处(LINQ 主要)。

使用@i3arnon 的“长答案”我创建了这个扩展方法:

public static async IAsyncEnumerable<T> ToAsyncEnumerable<T>(this IAsyncCursor<T> asyncCursor)
{
    while (await asyncCursor.MoveNextAsync())
    {
        foreach (var current in asyncCursor.Current)
        {
            yield return current;
        }
    }
}

由于 C# 9 中 GetAsyncEnumerator 扩展方法的支持和 foreach 的鸭子类型实现,现在可以使用以下扩展方法直接迭代游标:

public static class MongoDbCursorExtensions
{
    public static IAsyncCursor<T> GetAsyncEnumerator<T>(this IAsyncCursor<T> cursor) => cursor;
}

用法:

var cursor = await collection.Find(filter).ToCursorAsync();
await foreach (var batch in cursor) // extension method implicitly called here
{
    foreach (var item in batch)
    {
        // ...
    }
}

参见:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements#the-foreach-statement