You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RavenDB 4.0:如何为非ID属性添加自动递增功能

解决方案:RavenDB 4.0+实现自动递增属性

确实,RavenDB 4.0移除了IDocumentStoreListener和HiLoKeyGenerator这些旧API,不过实现自动递增属性(不管是文档ID还是自定义的OtherIncrementalId)还是有几种靠谱的方案,我给你详细说下:

1. 推荐方案:使用RavenDB计数器(Counters)

RavenDB的计数器是原子性的分布式操作,天生适合生成递增序列,完全避免并发冲突问题,这也是官方推荐的替代方式。

实现步骤:

  • 创建一个专门的计数器文档(比如counters/ProductIncrementalId)来维护递增序列
  • 在保存Product前,先递增计数器并获取新值,赋值给目标属性

代码示例:

首先封装一个获取递增ID的辅助方法:

private int GetNextProductIncrementalId(IDocumentSession session)
{
    // 递增指定计数器,参数分别是:计数器所属文档ID、计数器名称、增量值
    var counterResult = session.Counters.Increment("counters/ProductIncrementalId", "NextProductId", 1);
    // 同步session下直接取Value即可,异步场景用await
    return counterResult.Value;
}

然后在保存Product时调用:

using (var session = store.OpenSession())
{
    var newProduct = new Product();
    
    // 给自定义属性赋值递增ID
    newProduct.OtherIncrementalId = GetNextProductIncrementalId(session);
    
    // 如果需要生成递增的文档ID(比如products/1、products/2)
    newProduct.Id = $"products/{newProduct.OtherIncrementalId}";
    
    session.Store(newProduct);
    session.SaveChanges();
}

如果是集群环境,还可以用集群范围计数器,只需要把session.Counters换成session.ClusterCounters即可,完美适配多节点场景。


2. 备选方案:维护序列文档(需处理并发)

如果你不想用计数器,也可以手动维护一个序列文档来记录下一个递增ID,不过这种方式需要处理乐观并发冲突。

代码示例:

首先定义序列文档类:

public class ProductSequence
{
    public string Id { get; set; } = "sequences/ProductSequence";
    public int NextId { get; set; } = 1;
}

然后在保存Product时更新序列:

using (var session = store.OpenSession())
{
    // 加载或创建序列文档
    var productSequence = session.Load<ProductSequence>("sequences/ProductSequence") 
                          ?? new ProductSequence();
    
    var nextId = productSequence.NextId;
    // 更新序列的下一个ID
    productSequence.NextId++;
    session.Store(productSequence);
    
    // 创建Product并赋值递增ID
    var newProduct = new Product
    {
        OtherIncrementalId = nextId,
        Id = $"products/{nextId}"
    };
    session.Store(newProduct);
    
    try
    {
        session.SaveChanges();
    }
    catch (ConcurrencyException)
    {
        // 处理并发冲突:比如重试操作,或者告知用户稍后再试
        // 这里可以根据业务场景实现重试逻辑
        throw new InvalidOperationException("并发冲突,请重试");
    }
}

这种方式的缺点是高并发场景下可能会触发并发异常,需要额外处理重试逻辑,所以更推荐第一种计数器方案。


3. 自定义文档ID的另一种思路:使用IDocumentStore.OnBeforeStore事件

如果只是需要生成递增的文档ID,还可以通过注册OnBeforeStore事件来自动处理,结合计数器或者序列文档:

// 在初始化DocumentStore时注册事件
store.OnBeforeStore += (sender, args) =>
{
    if (args.Entity is Product product && string.IsNullOrEmpty(product.Id))
    {
        using (var session = store.OpenSession())
        {
            var nextId = session.Counters.Increment("counters/ProductIncrementalId", "NextProductId", 1).Value;
            product.Id = $"products/{nextId}";
        }
    }
};

这样每次保存Product时,如果ID为空,就自动生成递增ID,无需手动处理。


内容的提问来源于stack exchange,提问作者psManu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:15:46