【问题标题】:How is an IAsyncCursor used for iteration with the mongodb c# driver?IAsyncCursor 如何与 mongodb c# 驱动程序一起用于迭代?
【发布时间】:2021-12-30 19:53:26
【问题描述】:

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

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

【问题讨论】:

    标签: c# .net mongodb async-await mongodb-.net-driver


    【解决方案1】:

    您有 3 个选项:

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

    驱动程序有一些IAsyncCursor 的类似LINQ 的扩展方法,如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(并且每次迭代都会获得一批,而不仅仅是单个项目)。所以你可以自己实现整个usingwhile循环:

    IAsyncCursorSource<int> cursorSource = null;
    
    using (var asyncCursor = await cursorSource.ToCursorAsync())
    {
        while (await asyncCursor.MoveNextAsync())
        {
            foreach (var current in asyncCursor.Current)
            {
                // use current
            }
        }
    }
    

    【讨论】:

    • 是否保存修改ForEachAsync中的文档并使用ReplaceOneAsync将其保存回db?这种情况下的修改使用起来很复杂UpdateManyAsync
    • @hansmaad 是的。这是安全的。只是效率较低。
    【解决方案2】:

    我个人喜欢将光标转换为 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;
            }
        }
    }
    

    【讨论】:

    【解决方案3】:

    感谢 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

    【讨论】:

      猜你喜欢
      • 2018-01-13
      • 2015-04-16
      • 1970-01-01
      • 2021-12-04
      • 1970-01-01
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多