在 MongoDB 中安全地插入或更新子文档

Safely insert or update subdocument in MongoDB

我有一个 MongoDB 用于记录带有子项数组的项。添加或更新 SubItems 时,我首先 FindItem,然后我将新的 SubItem 添加到 SubItems 的数组中并替换整个 Item。在我开始批量插入 SubItems 之前,这一直有效 "great"。

我认为我的问题是查找和更新不是一个原子操作,结果是我输了 SubItems

我正在使用 .NET MongoDB.Driver,我的保存方法如下所示:

public Task Save(string itemId, SubItem subItem)
{
    var itemFilter = Builders<Item>.Filter.Eq(v => v.Id, itemId);
    var collection = _db.GetCollection<Item>("Items");

    var item = await collection.Find(itemFilter).SingleOrDefaultAsync();

    item.SubItems.Add(subItem);
    collection.ReplaceOneAsync(itemFilter, item, new UpdateOptions() { IsUpsert = true }).Wait();
    return Task.FromResult(0);
}

这是我的数据模型:

public class Item
{
    public string Id { get; set; }
    public List<SubItem> SubItems { get; set; }
}

public class SubItem
{
    public string Id { get; set; }
}

有没有一种方法可以在一次操作中插入或更新 SubItem,这样即使我有多个进程试图更新文档,我也可以确保保持整个 Item 文档的一致性同一时间?

您是否看过 AddToSet 方法,如果您将此方法与更新函数而不是替换函数结合使用,它应该可以更好地控制原子性。

var updateBuilder = Builders<Item>.Update.AddToSet(items => items.SubItems, new SubItem());

collection.UpdateOne(itemFilter, updateBuilder);

就像你的情况一样。

    public Task Save(string itemId, SubItem subItem)
    {
        var itemFilter = Builders<Item>.Filter.Eq(v => v.Id, itemId);
        var collection = _db.GetCollection<Item>("Items");

        var updateBuilder = Builders<Item>.Update.AddToSet(items => items.SubItems, subItem);

        collection.UpdateOneAsync(itemFilter, updateBuilder, new UpdateOptions() { IsUpsert = true }).Wait();
    }