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
相关产品推荐
相关产品推荐

