应使用官方 mongodb.driver(.net standard 2.0+),避免 legacy 驱动;用 poco 类而非 dynamic/bsondocument;单条查询优先 firstordefaultasync;分页慎用 skip/limit;非幂等操作需关闭 retrywrites 并手动重试。

用哪个驱动?别选错 MongoDB.Driver 版本
现在唯一靠谱的官方驱动是 MongoDB.Driver(.NET Standard 2.0+),不是旧版 MongoDB.Driver.Legacy,也不是第三方封装。选错会导致连接失败、异步方法缺失、BsonDocument 解析异常等——尤其当你看到 NotSupportedException: This driver does not support synchronous operations,基本就是还在用 Legacy 驱动。
- 项目中通过 NuGet 安装最新稳定版
MongoDB.Driver(当前主流是 2.2x),不要手动引用 DLL - .NET 6/7/8 项目必须用 2.19+,否则
FindAsync在某些 LINQ 场景下会静默跳过过滤条件 - 如果用了
Microsoft.Extensions.DependencyInjection,优先注册IMongoClient单例,别在每次请求里 newMongoClient
IMongoCollection<t></t> 的泛型类型怎么定?别硬套 dynamic 或 BsonDocument
直接用 BsonDocument 看似灵活,但很快会掉进序列化陷阱:字段名大小写不一致、嵌套对象反序列失败、日期被转成字符串。用 dynamic 更糟——编译期零检查,运行时报 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException 是家常便饭。
- 定义明确的 POCO 类,用
[BsonId]、[BsonElement("user_name")]控制映射,比靠约定更稳 - 如果文档结构高度动态(比如用户自定义表单),至少用
Dictionary<string bsonvalue></string>,它能准确承载ObjectId、DateTime、null等原生类型 - 避免在同一个集合里混存结构差异极大的文档——MongoDB 不校验 schema,但你的
Find<t>()</t>会因字段缺失而默认填充默认值(如int变 0),查不到数据还找不到原因
FindAsync 和 FirstOrDefaultAsync 性能差在哪?别无脑 await 所有查询
表面上都是异步,但 FindAsync 返回的是 IAsyncCursor<t></t>,真正拉数据发生在 ToListAsync 或 ForEachAsync 时;而 FirstOrDefaultAsync 内部会加 .Limit(1) 并尽早终止游标,网络和服务器开销小得多。
- 只取一条?用
FirstOrDefaultAsync,别先FindAsync再ToListAsync().FirstOrDefault()—— 这会把整个匹配结果集从服务端拖到内存 - 需要分页?用
FindAsync+Skip/Limit,但注意Skip越大越慢,10 万条后翻页建议改用_id > lastId游标式分页 - 带排序的查询务必在对应字段建索引,否则
Sort().Limit(10)可能扫全表——用ExplainAsync看执行计划,确认executionStats.nReturned接近limit值才算合理
连接字符串里的 retryWrites=true 到底要不要关?
默认开启,对大多数业务写操作是安全的,但它会让 MongoDB 在网络闪断时自动重试(最多 1 次)。问题在于:如果重试前你已经发了 HTTP 响应给前端,用户点提交没反应,刷新页面又提交一次,就可能产生重复数据。
- 非幂等操作(如“扣库存”“发通知”)必须自己控制重试逻辑,而不是依赖驱动层的
retryWrites - 连接字符串里显式加上
&retryWrites=false,并在业务层用Polly或简单 while 循环做有条件重试(比如仅对ConnectionTimeout或SocketException) - 副本集部署下,
readPreference=primaryPreferred比默认的primary更容错,但要注意最终一致性窗口——刚写入的数据可能在从节点查不到
真正麻烦的从来不是连不上数据库,而是连上了却读到旧数据、写进去重复记录、或者以为成功了其实超时回滚了。这些边界情况不会报红字,只会悄悄污染业务状态。











