让我们看一下向标准 MongoCollection<T> 实例对象发出的以下查询:
return _bufferCollection.Find(
GetNextBlockQuery(lastTick, lastRevisionId))
.OrderBy(d => d.LastUpdated)
.ThenBy(d => d.RevisionIdNumeric);
GetNextBlockQuery 方法仅返回一个用 C# mongo 查询语法表示的 Query<T> 查询对象。在此查询中 ,使用标准 LINQ 语法简单地对 Find() 方法的结果进行了排序 。
你发现问题出在哪里了吗?
Find() 方法返回一个 MongoCursor<T> 类型的对象,它实现了 IEnumerable<T> 但没有实现 IQueryable<T> 。
如果使用 AsQueryable() 扩展方法通过 LINQ 查询 MongoCollection,则使用 OrderBy() 或 ThenBy() LINQ 扩展方法没有问题。在这种情况 下,Mongo C# 驱动程序中 IQueryable 的实现会将所有内容转换为标准的 mongo 查询语法, 然后它向服务器执行转换后的查询并将对象返回给调用者。
在前面的示例中,针对 MongoCursor 调用了 OrderBy() LINQ 运算符, 排序将在内存中完成。 问题是:OrderBy 方法将对 IEnumerable 对象进行操作并迭代所有对象以按正确的顺序返回它们。
如果针对标准 MongoCursor 使用 LINQ 运算符,它将在内存中运行,从而影响性能。
这会损害应用程序的性能:每次执行查询时, 整个结果集都会加载到内存中,然后进行排序。 为避免此问题,您不需要将本机 Mongo C# 查询与 LINQ 运算符混合使用。正确的查询如下:
return _bufferCollection.Find(
GetNextBlockQuery(lastTick, lastRevisionId))
.OrderBy(d => d.LastUpdated)
.ThenBy(d => d.RevisionIdNumeric);
这个新版本使用 Mongo C# Query 的 SetSortOrder() 方法,因此它将直接从 Mongo 服务器排序,并且对象将在标准的 for-each 枚举期间加载到内存中。 如果你想限制返回对象的数量,上面的问题真的很糟糕。 如果使用 Take(50) 方法只获取 50 个对象,实际上是将整个集合加载到内存中,然后返回前 50 个元素。这与直接在查询中要求 mongo 只返回 50 个元素是完全不同的。
最大的问题之一是,如果您在第一次查询时使用 LINQ 运算符 Take() 限制记录数,那么您将进行客户端分页,从而导致性能损失显着。
作为一般规则,避免混合使用 LINQ 和 Mongo 查询类来向您的 Mongo 服务器发出查询,并且 比 LINQ 更喜欢本机查询语法,因为它会为您提供 Mongo 的全部功能 。相反,LINQ 查询将仅公开可能查询的一个子集,并注意 Select 运算符在内存中运行,而不是直接从服务器限制返回字段的数量。