在平时的开发中,InsertOne、UpdateOne、Find、DeleteOne 这四个方法几乎覆盖了 MongoDB 的日常操作,但很多人直接套用示例代码时,往往会遇到空引用异常或无效操作异常——根本原因不是语法错误,而是驱动版本升级后,BsonDocument 的构造逻辑和异步支持方式发生了变化。

用 InsertOneAsync 而不是 Insert,否则会阻塞线程
旧版驱动(比如 1.x 时代)允许同步插入:collection.Insert(doc);但 2.10+ 版本已经彻底移除了这个方法,只保留异步入口。如果强行用同步包装(比如 .Result),在 ASP.NET Core 中极易引发死锁。
- 必须用
await collection.InsertOneAsync(doc),且所在方法需标记async Task doc可以是BsonDocument,也可以是强类型实体(如new User { Name = "Alice" }),驱动会自动完成序列化- 插入后想获取生成的
_id,别再手动 newObjectId()了:用result.InsertedId拿返回值即可
Find 查询时过滤器写法不匹配,查不到数据却无报错
Find 方法接受 FilterDefinition,不是随便传个 BsonDocument 就行。常见错误是把 JSON 字符串或裸 BsonDocument 直接塞进去,结果过滤器被忽略,返回全量数据(或者空结果)。
- 正确写法:
collection.Find(Builders.Filter.Eq(u => u.Status, "active")) - 复合条件用
&连接:Builders.Filter.Eq(...). & Builders .Filter.Gt(...) - 如果坚持用
BsonDocument,必须转成FilterDefinition:collection.Find(new BsonDocument("status", "active"))是错的;应写collection.Find(FilterDefinition.Parse("{status: 'active'}"))
DeleteOne 和 DeleteMany 的返回值容易被忽略
删除操作不抛异常,绝不等于成功删除了文档。驱动返回 DeleteResult,其中 DeletedCount 才是真实删除数量——常见陷阱是只检查是否抛异常,却没验证 result.DeletedCount > 0。
- 按 ID 删除时,务必确认传入的是
ObjectId类型,不是字符串:new ObjectId(id);否则过滤器匹配失败,DeletedCount恒为 0 - 批量删除慎用
DeleteMany:没有事务回滚,删错无法撤回;生产环境建议先CountDocuments预估数量 - 删除后不刷新缓存或通知下游服务,会导致状态不一致——这不是驱动问题,但常出现在实际操作链路里
更新操作必须显式指定字段,$set 不是默认行为
UpdateOne 默认是“替换整个文档”,不是“局部更新”。如果传一个不带 _id 的实体进去,原 _id 会被丢弃,MongoDB 自动生成新 _id,旧文档其实还在。
- 局部更新必须用
Builders.Update.Set(u => u.Name, "Bob") - 一次更新多个字段:
Builders.Update.Set(...).Set(...).Set(...) - 想清空某个字段?用
Unset:Builders.Update.Unset(u => u.A vatarUrl) - 更新前没加过滤条件(比如漏写
Eq(u => u.Id, id)),可能误改全表
真正难的不是写对某一行代码,而是每个操作背后隐含的语义约束:插入是否要校验唯一索引、查询是否用了未建索引的字段、更新是否触发了 TTL 或变更流——这些不会在编译时报错,但上线后立刻暴露。