2017-09-17 91 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(); 
    } 
相關問題