【问题标题】:Creating a sequence -- SetOnInsert appears to do nothing创建一个序列——SetOnInsert 似乎什么都不做
【发布时间】:2021-02-17 09:17:51
【问题描述】:

我在尝试时遇到问题,归结为增加文档中的字段插入整个文档。上下文是“尝试为序列插入初始文档或增加现有序列的序列号”。

这段代码:

private async Task<int> GetSequenceNumber(string sequenceName)
{
    var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
    var builder = Builders<Sequence>.Update;
    var update = builder
        .SetOnInsert(x => x.CurrentValue, 1000)
        .Inc(x => x.CurrentValue, 1);

    var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
        filter, 
        update, 
        new FindOneAndUpdateOptions<Sequence>
        {
            IsUpsert = true, 
            ReturnDocument = ReturnDocument.After,
        });

    return sequence.CurrentValue;
}

导致异常

MongoDB.Driver.MongoCommandException:命令 findAndModify 失败:更新路径“currentvalue”会在“currentvalue”处产生冲突。 在 MongoDB.Driver.Core.WireProtocol.CommandUsingCommandMessageWireProtocol`1.ProcessResponse(ConnectionId connectionId, CommandMessage responseMessage)

删除 SetOnInsert 不会导致错误,但会插入一个 currentValue 等于 1 而不是预期的 1000 的文档。

如果SetOnInsert 没有被兑现,它几乎会出现,并且正在发生的事情是插入默认文档然后 currentValue 通过Inc 原子地递增为新文档已创建。

如何克服这些问题?非 C# 解决方案也将受到欢迎,因为我可以翻译...

【问题讨论】:

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


【解决方案1】:

好的,感谢 cmets 中的@dododo,我现在意识到IncSetOnInsert 不能同时应用。这是不直观的,因为您认为前者仅适用于更新,而后者仅适用于插入。

我采用了下面的解决方案,它遭受了不止一次的往返,但至少有效,并且似乎适用于我的基于并发的测试。

public async Task<int> GetSequenceNumber(string sequenceName, int tryCount)
{
    if (tryCount > 5) throw new InvalidOperationException();

    var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
    var builder = Builders<Sequence>.Update;

    // optimistically assume value was already initialized
    var update = builder.Inc(x => x.CurrentValue, 1);

    var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
        filter, 
        update, 
        new FindOneAndUpdateOptions<Sequence>
        {
            IsUpsert = true, 
            ReturnDocument = ReturnDocument.After,
        });

    if (sequence == null)
        try
        {
            // we have to try to save a new sequence...
            sequence = new Sequence { Id = sequenceName, CurrentValue = 1001 };
            await _context.SequenceNumbers.InsertOneAsync(sequence);
        }
        // ...but something else could beat us to it
        catch (MongoWriteException e) when (e.WriteError.Code == DuplicateKeyCode)
        {
            // ...so we have to retry an update
            return await GetSequenceNumber(sequenceName, tryCount + 1);
        }

    return sequence.CurrentValue;
}

我确定还有其他选择。例如,可以使用聚合管道。

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 2015-10-29
    • 2012-09-12
    • 2011-03-17
    • 2017-09-11
    • 2013-05-31
    • 2016-09-22
    • 2014-08-14
    • 1970-01-01
    相关资源
    最近更新 更多