创建一个序列——SetOnInsert 似乎什么都不做

Creating a sequence -- SetOnInsert appears to do nothing

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

此代码:

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: Command findAndModify failed: Updating the path 'currentvalue' would create a conflict at 'currentvalue'. at MongoDB.Driver.Core.WireProtocol.CommandUsingCommandMessageWireProtocol`1.ProcessResponse(ConnectionId connectionId, CommandMessage responseMessage)

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

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

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

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

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

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;
}

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