You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MongoDB C#中对嵌套数组对象同时进行新增与更新?

在C#中实现MongoDB主字段更新+嵌套数组的新增/更新(存在则更,无则新增)

需求说明

需要同时完成两个操作:

  1. 更新文档的serviceName、intervalKm、dueSoonKm等主字段;
  2. 对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:47:04