先用 MongoClient 连接 MongoDB 并调用 ListDatabaseNames() 确认连通性,再检查服务状态、连接字符串(含 retryWrites 和 w=majority)、Atlas 白名单及 URL 编码;泛型 T 决定序列化行为,需注意属性可访问性、ObjectId 映射、DateTime 时区;InsertOneAsync 与 ReplaceOneAsync 语义不同,慎用;查询务必加 Limit 或分页,优先建索引并避免滥用 LINQ。

怎么用 MongoClient 连上 MongoDB 并确认连通性
连不上是最常见的卡点,不是代码写错,而是连接字符串或服务状态不对。别急着写增删改查,先让 MongoClient 成功握手。
常见错误现象:MongoConnectionException、Timeout after 30000ms、Unable to connect to server
- 检查 MongoDB 服务是否真在运行(
mongod进程或 Docker 容器状态),本地默认端口是27017 - 连接字符串里别漏
/?retryWrites=true&w=majority(尤其 MongoDB 6+ 默认要求),否则可能静默失败 - 如果连 Atlas,确保 IP 白名单已加,且连接字符串里的用户名密码已 URL 编码(比如
@要换成%40) - 用
await client.ListDatabaseNames().ToListAsync()主动触发一次请求,比只 new 出MongoClient更可靠
IMongoCollection 怎么选对泛型类型和序列化行为
泛型参数 T 不只是“看着顺眼”,它直接决定 BSON 序列化规则、字段映射、甚至查询能否命中索引。
使用场景:你有一组 C# 类,要存进 users 集合,但发现字段名对不上、时间变零值、或者 Find(x => x.Id == id) 查不到
- 类属性必须有 public get/set(只读属性不会被序列化),或显式加
[BsonElement("user_name")]映射 -
ObjectId字段建议用BsonId+BsonRepresentation(BsonType.ObjectId),否则字符串 ID 会被当普通字符串存,查不出来 - 避免用
DateTimeOffset—— MongoDB 原生只存 UTCDateTime,序列化时会丢时区信息;统一用DateTime并设为DateTimeKind.Utc - 如果类里有字典、嵌套对象或接口类型,得注册自定义序列化器,否则运行时报
System.NotSupportedException: Type is not supported
写入数据时,InsertOneAsync 和 ReplaceOneAsync 别混用
看起来都是“存一条”,但语义和副作用完全不同。用错会导致数据意外丢失或版本混乱。
常见错误现象:重复执行后文档字段越来越少、更新后某些字段变 null、并发写入时数据被覆盖
-
InsertOneAsync只在 _id 不存在时插入;如果 _id 已存在,直接抛MongoWriteException(不是静默忽略) -
ReplaceOneAsync是全量替换 —— 传进去的对象里没写的字段,数据库里对应字段会被删掉(除非加IsUpsert = true且原文档存在) - 想“有就更新部分字段、没有就插入”,用
UpdateOneAsync+Builders,而不是靠 Replace 撑场面.Update.Set(...) - 批量操作优先用
InsertManyAsync/UpdateManyAsync,别循环调InsertOneAsync—— 网络往返和锁开销大得多
查询性能差?先看 Find 后有没有 .ToList() 或 .FirstOrDefaultAsync()
很多人以为 collection.Find(x => x.Status == "active") 就发了查询,其实这只是构建了一个可延迟执行的 IAsyncCursor。真正拖慢的,是后续不加节制的枚举。
性能影响:没加限制的 .ToList() 可能把几万条全拉到内存;.CountDocumentsAsync() 在无索引字段上会扫全表
- 永远给
Find加.Limit(100)或明确的分页(.Skip().Limit()),尤其在管理后台列表页 - 用
.FirstOrDefaultAsync()替代.ToListAsync().FirstOrDefault()—— 前者只取第一条,后者先拉全量再取首项 -
CountDocumentsAsync比EstimatedDocumentCountAsync精确但慢;如果只要大概数量(比如分页总数),后者快一个数量级 - 确保查询字段建了索引:用
collection.Indexes.CreateOneAsync(new CreateIndexModel(Builders .IndexKeys.Ascending(x => x.Email)))
最常被忽略的是:MongoDB.Driver 的 LINQ 支持有限,.Where(x => x.Name.Contains("abc")) 会转成正则,没索引就扫全表;不如直接用 Builders 并明确加索引。











