MongoDB更新嵌套属性报错:无法在null的Address字段创建子属性
问题描述
我定义了以下C#类:
public class User { public Guid? Id { get; set; } public String? Name { get; set; } public Address? Address { get; set; } }
以及Address类:
public class Address { public String? Street { get; set; } public String? City { get; set; } public String? State { get; set; } }
我在实现文档的部分更新功能,比如更新用户的City和State时,用了以下代码:
var filter = Builders<BsonDocument>.Filter.Eq("Id", id); var updates = new List<UpdateDefinition<BsonDocument>>(); updates.Add(Builders<BsonDocument>.Update.Set("Address.City", "New City Value")); updates.Add(Builders<BsonDocument>.Update.Set("Address.State", "New State Value")); var update = Builders<BsonDocument>.Update.Combine(updates); var bsonDocument = await collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions<BsonDocument> { ReturnDocument = ReturnDocument.After });
这段代码在Address字段不为null时能正常工作,但当用户的Address字段为null时,会抛出错误:
MongoDB.Driver.MongoCommandException: Command findAndModify failed: Cannot create field 'City' in element {Address: null}.
我需要确保Address对象会被创建,从而成功设置City和State属性,而且不需要先从数据库查询当前对象。
解决方案
可以利用MongoDB的$mergeObjects操作符结合$set实现需求,它能合并现有Address对象(如果存在且不为null)与新属性值;若Address为null,则直接创建新的Address对象,无需提前查询文档。
方案1:BsonDocument字符串写法
var filter = Builders<BsonDocument>.Filter.Eq("Id", id); // 用$mergeObjects合并现有Address(或空对象)与新的City、State var updateDefinition = Builders<BsonDocument>.Update.Set( "Address", BsonDocument.Parse("{ $mergeObjects: [ { $ifNull: [ '$Address', {} ] }, { City: 'New City Value', State: 'New State Value' } ] }") ); var bsonDocument = await collection.FindOneAndUpdateAsync(filter, updateDefinition, new FindOneAndUpdateOptions<BsonDocument> { ReturnDocument = ReturnDocument.After });
方案2:强类型写法(更安全)
避免直接写Bson字符串,用强类型方式构建更新定义,减少拼写错误:
var filter = Builders<User>.Filter.Eq(u => u.Id, id); var update = Builders<User>.Update.Set(u => u.Address, new BsonDocument("$mergeObjects", new BsonArray { new BsonDocument("$ifNull", new BsonArray { "$Address", new BsonDocument() }), new Address { City = "New City Value", State = "New State Value" }.ToBsonDocument() }) ); var updatedUser = await collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions<User> { ReturnDocument = ReturnDocument.After });
逻辑说明
$ifNull: [ '$Address', {} ]:判断当前Address字段,若为null或不存在则返回空对象{},否则返回原Address对象。$mergeObjects:将前面得到的对象与包含新City、State的对象合并,既保留原Address中已有的Street等属性(如果存在),又更新目标字段;若原Address为null,则直接生成包含新属性的Address对象。
内容的提问来源于stack exchange,提问作者ScubaSteve
相关产品推荐
相关产品推荐

