MongoDB驱动中按Key获取唯一对象的C# LINQ实现问题
解决C# MongoDB LINQ聚合按Key取唯一对象的问题
你遇到的问题根源在于:MongoDB的LINQ驱动无法将LINQ标准库的FirstOrDefault()方法转换为对应的MongoDB聚合操作符,而你原来的MongoDB原生聚合用的是$first操作符来获取分组内的第一个文档。
不用手动写BsonDocument,我们可以用MongoDB.Driver提供的**聚合累加器(Accumulators)**来实现和原生聚合完全一致的逻辑,这是纯LINQ的写法:
using MongoDB.Driver; using MongoDB.Driver.Linq; public static IAggregateFluent<T> WhereDistinct<T, TKey>(this IMongoCollection<T> collection, FilterDefinition<T> filter, Expression<Func<T, TKey>> distinctField) { return collection.Aggregate() .Match(filter) .Group( keySelector: distinctField, groupProjection: g => new { Value = Accumulators.First(g, x => x) // 对应MongoDB的$first: "$$ROOT" }) .ReplaceRoot(arg => arg.Value); }
关键说明:
- *Accumulators.First(g, x => x)*会被驱动转换为MongoDB聚合中的
$first: "$$ROOT",和你原来的原生逻辑完全匹配,它会获取分组内的第一个文档(因为你提到不关心取分组里的哪一个,所以$first完全满足需求)。 - 这里不需要用
FirstOrDefault(),因为经过Match过滤后的分组至少会有一个文档,$first足够覆盖你的场景;如果确实需要处理空分组的情况,Accumulators.First在分组为空时会返回null,和FirstOrDefault()的行为一致。
验证对应的聚合管道
这个LINQ写法生成的聚合管道和你原来的原生命令完全一致:
[ { $match: <你的过滤条件> }, { $group: { _id: <distinctField>, Value: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$Value" } } ]
这样既保持了LINQ的简洁性,又完美复现了你原来的MongoDB聚合逻辑,不会再出现表达式树不支持的异常。
内容的提问来源于stack exchange,提问作者Alex Zhukovskiy
相关产品推荐
相关产品推荐

