如何在MongoDB C#中对嵌套数组对象同时进行新增与更新?
在C#中实现MongoDB主字段更新+嵌套数组的新增/更新(存在则更,无则新增)
需求说明
需要同时完成两个操作:
- 更新文档的
serviceName、intervalKm、dueSoonKm等主字段; - 对
vehicle嵌套数组执行存在则更新,不存在则新增的操作(以vehicleId作为匹配标识)。
示例MongoDB文档
{ "serviceName": "Service1", "intervalKm": 3219, "dueSoonKm": 161, "vehicle": [ { "vehicleId": "9100ffff-48a9-ca63-e418-22ca712b0000", "dueAtKm": 3228, "untilDueKm": 3219 }, { "vehicleId": "9100ffff-48a9-ca63-e430-22ca712b0000", "dueAtKm": 3219, "untilDueKm": 3219 } ] }
现有代码局限
现有代码仅能更新主字段,无法处理嵌套数组的新增/更新逻辑:
var result = _mongoCollection.UpdateOneAsync( Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId), Builders<ServiceModel>.Update .Set(rec => rec.ServiceName, request.ServiceName) .Set(rec => rec.IntervalKm, request.IntervalKm) .Set(rec => rec.DueSoonKm, request.DueSoonKm) .ConfigureAwait(false);
解决方案
方式一:分步骤处理(单条操作)
先更新主字段,再逐个处理vehicle数组元素的更新/新增,逻辑清晰易调试:
// 1. 更新主字段 var mainUpdate = Builders<ServiceModel>.Update .Set(rec => rec.ServiceName, request.ServiceName) .Set(rec => rec.IntervalKm, request.IntervalKm) .Set(rec => rec.DueSoonKm, request.DueSoonKm); await _mongoCollection.UpdateOneAsync( Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId), mainUpdate, new UpdateOptions { IsUpsert = false } ).ConfigureAwait(false); // 2. 处理每个Vehicle的更新/新增 foreach (var reqVehicle in request.Vehicles) { // 构建过滤条件:匹配目标文档+匹配对应vehicleId的数组元素 var vehicleFilter = Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId) & Builders<ServiceModel>.Filter.Eq("vehicle.vehicleId", reqVehicle.VehicleId); // 构建数组元素更新操作 var vehicleUpdate = Builders<ServiceModel>.Update .Set("vehicle.$.dueAtKm", reqVehicle.DueAtKm) .Set("vehicle.$.untilDueKm", reqVehicle.UntilDueKm); // 执行更新,获取结果 var updateResult = await _mongoCollection.UpdateOneAsync( vehicleFilter, vehicleUpdate, new UpdateOptions { IsUpsert = false } ).ConfigureAwait(false); // 如果没有匹配到任何文档,说明该vehicleId不存在,执行新增 if (updateResult.MatchedCount == 0) { var addVehicleUpdate = Builders<ServiceModel>.Update .Push(rec => rec.Vehicle, reqVehicle); await _mongoCollection.UpdateOneAsync( Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId), addVehicleUpdate ).ConfigureAwait(false); } }
方式二:批量操作(高效处理多元素)
使用BulkWrite批量提交所有操作,减少网络请求次数,适合处理大量vehicle元素的场景:
var writeOperations = new List<WriteModel<ServiceModel>>(); // 添加主字段更新操作 writeOperations.Add(new UpdateOneModel<ServiceModel>( filter: Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId), update: Builders<ServiceModel>.Update .Set(rec => rec.ServiceName, request.ServiceName) .Set(rec => rec.IntervalKm, request.IntervalKm) .Set(rec => rec.DueSoonKm, request.DueSoonKm) )); // 为每个Vehicle添加更新/新增操作 foreach (var reqVehicle in request.Vehicles) { // 操作1:尝试更新已有vehicleId的元素 writeOperations.Add(new UpdateOneModel<ServiceModel>( filter: Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId) & Builders<ServiceModel>.Filter.Eq("vehicle.vehicleId", reqVehicle.VehicleId), update: Builders<ServiceModel>.Update .Set("vehicle.$.dueAtKm", reqVehicle.DueAtKm) .Set("vehicle.$.untilDueKm", reqVehicle.UntilDueKm) )); // 操作2:如果更新未命中,新增该Vehicle元素 writeOperations.Add(new UpdateOneModel<ServiceModel>( filter: Builders<ServiceModel>.Filter.Eq(p => p.ServiceId, serviceId) & Builders<ServiceModel>.Filter.Not(Builders<ServiceModel>.Filter.Eq("vehicle.vehicleId", reqVehicle.VehicleId)), update: Builders<ServiceModel>.Update.Push(rec => rec.Vehicle, reqVehicle) )); } // 执行批量操作 await _mongoCollection.BulkWriteAsync(writeOperations).ConfigureAwait(false);
关键说明
- 使用
vehicle.$定位符:MongoDB会自动匹配过滤条件中第一个符合的数组元素,实现精准更新; - 批量操作注意事项:
BulkWrite会按顺序执行所有操作,确保更新和新增逻辑不会冲突; - 实体类匹配:确保
ServiceModel和VehicleModel的属性名与MongoDB文档字段名一致(可通过[BsonElement]特性映射)。
内容的提问来源于stack exchange,提问作者IndRishikesh
相关产品推荐
相关产品推荐

