mongodb.driver 2.20+ 配合强类型 poco 是最稳路径;必须 await 异步操作、用 builders 构建过滤器、insertoneasync 后取 result.insertedid、updateone 要局部更新需显式 set,连接后须调用 listdatabasenames() 确认连通。

用 MongoDB.Driver 2.20+ 版本配合强类型 POCO 类,是当前最稳的文档操作路径;硬套老教程的同步写法、BsonDocument 直接拼串、或忽略 await,90% 会掉进空结果、字段丢失、ID 匹配失败的坑里。
连接 MongoDB 后怎么确认真连上了?别只 new MongoClient
驱动不主动连,直到你第一次发请求才握手。光 new MongoClient(connectionString) 不报错 ≠ 连通。
- 必须主动触发一次服务端调用,比如
await client.ListDatabaseNames().ToListAsync() - 本地开发先确认
mongod进程在跑(Windows 查服务,macOS/Linux 执行ps aux | grep mongod) - 连 Atlas 时,检查连接字符串里的用户名密码是否做了
Uri.EscapeDataString编码(比如@要变%40) - 连接字符串末尾加上
?retryWrites=true&w=majority,MongoDB 6+ 默认要求,漏了可能静默失败
InsertOneAsync 和 UpdateOne 的语义陷阱:不是“插入/更新”,是“插入整文档/替换整文档”
传一个没带 _id 的实体进 UpdateOne,MongoDB 不会局部改字段,而是删旧建新——因为默认行为是全量替换。
- 要局部更新(比如只改
Status),必须用Builders<user>.Update.Set(u => u.Status, "inactive")</user> -
InsertOneAsync后拿 ID,别new ObjectId(),直接取result.InsertedId(类型是ObjectId,不是string) - 实体类里没加
[BsonId],又没手动赋值_id字段,插入后InsertedId是空的 -
ReplaceOneAsync和UpdateOne别混用:ReplaceOneAsync一定丢原_id;UpdateOne加上IsUpsert = true才能实现“有则更新、无则插入”
FilterDefinition 写不对,等于没写过滤器
collection.Find("{status: 'active'}") 看似像 MongoDB Shell 语法,实际会被当作文本忽略,返回全量数据或空——驱动根本不解析这串 JSON 字符串。
- 正确写法只有两种:
— 表达式构建:Builders<user>.Filter.Eq(u => u.Status, "active") & Builders<user>.Filter.Gt(u => u.CreatedAt, DateTime.UtcNow.AddDays(-7))</user></user>
— JSON 解析:FilterDefinition<user>.Parse("{status: 'active'}")</user> - 用
BsonDocument构造过滤器也得转成FilterDefinition:collection.Find(new BsonDocument("status", "active"))是错的 - 按字符串 ID 查询时,务必转成
ObjectId:collection.Find(Builders<user>.Filter.Eq("_id", new ObjectId(id)))</user>,否则匹配永远失败 - 字段名大小写敏感,POCO 属性名和 BSON 字段名不一致时,得加
[BsonElement("user_name")]映射
FindAsync 和 FirstOrDefaultAsync 别乱选,性能差十倍不止
FindAsync 返回的是游标 IAsyncCursor<t></t>,真正拉数据发生在 ToListAsync() 或 ForEachAsync() 阶段;而 FirstOrDefaultAsync 内部自动加 .Limit(1) 并尽早终止,网络开销小得多。
- 只查一条?用
await collection.FirstOrDefaultAsync(filter),别写(await collection.FindAsync(filter)).ToListAsync().Result.FirstOrDefault()——这会把所有匹配文档从服务端拖到内存 - 分页慎用
Skip/Limit,10 万条后翻页延迟飙升,建议改用游标式分页:collection.Find(x => x._id > lastId).Limit(10) - 带
Sort()的查询,务必在排序字段建索引,否则Sort().Limit(10)可能扫全表;用ExplainAsync()看执行计划 -
DeleteOne后别只看有没有异常,必须检查result.DeletedCount > 0;DeleteMany没事务回滚,生产环境删前先CountDocumentsAsync预估数量
最容易被忽略的点:所有异步方法(InsertOneAsync、FindAsync、UpdateOne)都必须 await,不等就继续往下走,结果就是文档没插、查不到、更新没生效——而且不报错,只默默失败。











