Android端MongoDB LINQ聚合查询随机崩溃问题求助
问题:MongoDB LINQ查询随机崩溃及转换为Builders实现方案
我在MongoDB Atlas上拥有一个记录已读书籍详情的数据库,包含authors和books两个集合,对应实体类代码如下:
[BsonIgnoreExtraElements] public class Author { [BsonId][BsonRepresentation(BsonType.ObjectId)] public ObjectId _id { get; set; } [BsonElement("first")] public string? first { get; set; } [BsonElement("last")] public string? last { get; set; } [BsonElement("author_id")] public BsonInt32? author_id { get; set; } [BsonElement("FullName")][BsonIgnoreIfNull] public string? FullName { get; set; } } [BsonIgnoreExtraElements] public class Book { [BsonId][BsonRepresentation(BsonType.ObjectId)] public ObjectId _id { get; set; } [BsonElement("title")] public string? title { get; set; } [BsonElement("title_id")] public BsonInt32? title_id { get; set; } [BsonElement("author")] public string? author { get; set; } [BsonElement("issue_date")] public string? issue_date { get; set; } [BsonElement("return_date")] public string? return_date { get; set; } }
数据库为MAUI开发的Android应用提供数据,其中一个页面需展示数据库中书量最多的作者列表,使用了如下LINQ分组查询:
public async Task<List<Popular>> GetPopular() { IMongoQueryable<Popular>? query = (from book in booksCollection.AsQueryable() group book by book.author into g let lastBook = g.OrderByDescending(b => b.return_date).FirstOrDefault() select new Popular { Author = g.Key, Count = g.Count(), ReturnDate = lastBook.return_date, Title = lastBook.title }).OrderByDescending(g => g.Count).ThenBy(g => g.Author).Take(10); List<Popular>? popular = await query.ToListAsync(); return popular; }
该查询多数时候能正常运行并生成列表,但会随机在query.ToListAsync()语句处崩溃,报错信息如下:
**MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers.AstGroupingPipelineOptimizer+UnableToRemoveReferenceToElementsException:** 'Exception of type 'MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers.AstGroupingPipelineOptimizer+UnableToRemoveReferenceToElementsException' was thrown.'
使用环境:Visual Studio 17.11.4,默认启用LINQ 3的MongoDB.Driver 2.9。请问是否可以将该查询轻松转换为使用Builders或BsonDocument实现,或者我的LINQ查询本身存在错误?
问题分析
这个异常是MongoDB LINQ 3优化器的已知bug,当分组后引用分组内排序后的元素时,优化器可能无法正确处理元素引用,导致随机崩溃。你的LINQ逻辑本身是正确的,但LINQ 3的实现存在局限性。
转换为Builders/BsonDocument实现
直接使用聚合管道(Aggregation Pipeline)可以避开LINQ优化器的问题,以下是等价的实现:
public async Task<List<Popular>> GetPopular() { var pipeline = new BsonDocument[] { // 按作者分组,统计书籍数量并保留所有书籍数据 new BsonDocument("$group", new BsonDocument { { "_id", "$author" }, { "count", new BsonDocument("$sum", 1) }, { "books", new BsonDocument("$push", "$$ROOT") } }), // 对每个分组内的书籍按return_date降序排序 new BsonDocument("$addFields", new BsonDocument { "sortedBooks", new BsonDocument("$sortArray", new BsonDocument { { "input", "$books" }, { "sortBy", new BsonDocument("return_date", -1) } }) }), // 获取排序后的第一本书 new BsonDocument("$addFields", new BsonDocument { "lastBook", new BsonDocument("$arrayElemAt", new BsonArray { "$sortedBooks", 0 }) }), // 投影出需要的字段 new BsonDocument("$project", new BsonDocument { { "Author", "$_id" }, { "Count", "$count" }, { "ReturnDate", "$lastBook.return_date" }, { "Title", "$lastBook.title" }, { "_id", 0 } }), // 按书籍数量降序、作者名称升序排序,取前10条 new BsonDocument("$sort", new BsonDocument { { "Count", -1 }, { "Author", 1 } }), new BsonDocument("$limit", 10) }; var result = await booksCollection.Aggregate<Popular>(pipeline).ToListAsync(); return result; }
额外优化建议
- 确保
return_date字段存储为日期类型(而非字符串):当前实体类中return_date是字符串类型,字符串排序可能存在逻辑错误,建议修改为DateTime?类型,并同步MongoDB中的字段类型,这样排序更准确。 - 创建索引提升性能:给
books集合的author字段创建索引,可大幅提升分组查询的效率:await booksCollection.Indexes.CreateOneAsync(new CreateIndexModel<Book>(Builders<Book>.IndexKeys.Ascending(b => b.author)));
内容的提问来源于stack exchange,提问作者user3017691
相关产品推荐
相关产品推荐

