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

时间:2017-09-17 22:29:40

标签: c# mongodb atomicity

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

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

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

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文档保持一致?

1 个答案:

答案 0 :(得分:2)

您是否查看了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();
    }