MongoDB - 替换嵌套数组中的项时的争用条件

时间:2017-03-25 09:01:24

标签: c# mongodb mongodb-.net-driver

open issue描述了无法将嵌套项的拉取和推送组合到单个更新中。相反,替换操作首先是拉动,然后是推动。

这个非孤立的交易似乎偶尔会导致重复,即并发交易都会在推送前执行拉动,最后会增加2个项目。

为了防范这种情况,我想出了一个解决方法:

public async Task ReplaceItem<TItem, TField>(string id, Expression<Func<TDocument, IEnumerable<TItem>>> field, Expression<Func<TItem, TField>> matchField, TField matchValue, TItem value)
{
    // can't update in single operation, see https://jira.mongodb.org/browse/SERVER-1050
    await PullItem(id, field, matchField, matchValue);

    // race condition on parallel requests -> duplicate detection built into query
    var filter = ClientFilter(id) & !Builders<TDocument>.Filter.ElemMatch(field, Builders<TItem>.Filter.Eq(matchField, matchValue));
    var update = Builders<TDocument>.Update.Push(field, value);
    var result = await _collection.UpdateOneAsync(filter, update);

    // ignore that document could have been removed between pull and push
    if (result.MatchedCount != 1)
        throw new RepositoryOperationException("Duplicate item found.");
}

public async Task PullItem<TItem, TField>(string id, Expression<Func<TDocument, IEnumerable<TItem>>> field, Expression<Func<TItem, TField>> matchField, TField matchValue)
{
    var update = Builders<TDocument>.Update.PullFilter(field, Builders<TItem>.Filter.Eq(matchField, matchValue));
    var result = await _collection.UpdateOneAsync(ClientFilter(id), update);

    if (result.MatchedCount != 1)
        throw new RepositoryOperationException("Document not found.");
}

这是否有意义还是有更好的策略? (我想避免将嵌套项重构为完整文档)

这是在Web API的上下文中运行的。

1 个答案:

答案 0 :(得分:0)

您可能希望利用并发模式来处理同步任务。

你可以使用一个监视器,锁是一个包装器。您将无法访问.Pulse()等。无论它是WebApi。

 private object obj = new object();
 ...
 lock(obj)
 {
    // Critical code
 }

或者你可以试试Balkin模式。

https://en.wikipedia.org/wiki/Concurrency_pattern