2017-09-17 95 views
1

我有一个MongoDB,在那里我保存了包含子项目数组的项目的记录。当添加或更新子项时,我首先Find主要Item,然后我将新的SubItem添加到SubItems的数组并替换整个Item。这工作“很好”,直到我开始批量插入SubItems安全地插入或更新MongoDB中的子文档

我想我的问题是,查找和更新不是一个原子操作,结果是我输了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文档一致,即使我有多个进程试图同时更新文档?

+0

“upserts”和阵列通常不会很好地混合。典型的情况是用多个操作(通常批量)“测试”数组中的项目,然后“推”新项目或“更新”它存在的位置。如果数组内容仅仅是数值或“单个数据”,你可以用['$ addToSet'](https://docs.mongodb.com/manual/reference/operator/update/addToSet/)“稍微” “财产。但是,如果存在多个属性,那么“唯一性”意味着这些属性在这种情况下的组合,并且它返回到“测试”方法。 –

+0

“upsert”问题是,当任何这样的更新需要“测试”数组内存在的元素时,“任何”负面结果意味着当你的实际意图可能是“追加”数组现有文件而不是新文件。所以你使用的实际过程实际上取决于你想要的模式。如果你真的想要“upserts”,并且你想要在数组中有多个属性,那么你实际上是要做“多个操作”。 –

回答

1

你看过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(); 
    } 
相关问题