MongoDB C#:向用户记录中ActiveAchievements的Public数组插入新元素失败,求排查原因
咱们一步步拆解你的代码问题,这几个关键点导致了更新失败:
1. 错误的过滤条件
你额外添加了Builders<T>.Filter.Eq("ActiveAchievements.Public", "Public")这个条件,但对照你的文档结构,ActiveAchievements.Public是一个数组,根本不是字符串"Public"!这个条件会直接导致没有任何文档被匹配到,更新操作自然就没效果了。其实你只需要通过_id定位用户就足够了,完全不需要这个多余且错误的过滤条件。
2. Update路径里误用了$占位符
$在MongoDB里是用来匹配数组中符合条件元素的占位符,但你的ActiveAchievements是一个嵌套对象(不是数组),所以这里根本不需要$。直接写"ActiveAchievements.Public"就能准确指向目标数组了。
3. 异步方法未等待
FindOneAndUpdateAsync是异步方法,你直接调用却不用await,会导致你的方法在数据库操作完成前就提前返回——看起来像是没生效,其实可能是操作还没执行完程序就结束了。必须加上await等待异步操作完成。
4. 泛型的小隐患(可选优化)
用typeof(T).Name作为集合名可能会踩坑(比如类名和实际集合名不一致),而且硬写字符串字段名很容易拼错。如果你的用户有对应的实体类,更推荐用强类型的表达式树来生成过滤和更新条件,这样更健壮。
修复后的代码
先给你最直接修复核心问题的版本:
public async Task Add<T>(string userId, string achievementId, int progress) { var collection = this.db.GetCollection<T>(typeof(T).Name); // 只保留_id的过滤条件 var filter = Builders<T>.Filter.Eq("_id", userId); // 修正Update路径,去掉多余的$ var update = Builders<T>.Update.Push("ActiveAchievements.Public", new ActiveAchievement() { _id = achievementId, Progress = progress }); // 等待异步操作完成 await collection.FindOneAndUpdateAsync(filter, update); }
如果用强类型实体类(比如你的用户类叫User),更推荐下面这种写法,避免字符串硬编码的错误:
public async Task Add(string userId, string achievementId, int progress) { // 明确指定集合名,比typeof(T).Name更可靠 var collection = this.db.GetCollection<User>("Users"); // 用表达式树生成过滤条件,类型安全 var filter = Builders<User>.Filter.Eq(u => u._id, userId); // 同样用表达式树指定Push的目标数组,不用担心字段名拼写错 var update = Builders<User>.Update.Push(u => u.ActiveAchievements.Public, new ActiveAchievement() { _id = achievementId, Progress = progress }); await collection.FindOneAndUpdateAsync(filter, update); }
这样修改后,应该就能成功往用户的ActiveAchievements.Public数组里添加元素了。
内容的提问来源于stack exchange,提问作者jjnrmason

