Raven为什么要为我的新文档生成一个已经存在的文档Id ?

本文关键字:文档 一个 Id 存在 为什么 我的 新文档 Raven | 更新日期: 2023-09-27 18:05:41

为什么这段代码(试图)用第二个对象覆盖我的第一个对象?我的第一个对象特别有一个Id为"StringBasedIdClasses/1",第二个对象不提供Id,所以Raven应该生成一个未使用的Id,不是吗?

 var quickStore = new EmbeddableDocumentStore() { RunInMemory = true };
 quickStore.Initialize();
 quickStore.RegisterListener(new DocumentConversionListener()).RegisterListener(new DocumentStoreListener());
 using (var session = quickStore.OpenSession())
 {
      session.Advanced.UseOptimisticConcurrency = true;
      var stringIdTest = new StringBasedIdClass()
      {
           Id = "StringBasedIdClasses/1",
           Name = "StringItem1"
      };
      session.Store(stringIdTest);
      session.SaveChanges();
 }
 using (var session = quickStore.OpenSession())
 {
      session.Advanced.UseOptimisticConcurrency = true;
      var stringIdTest = new StringBasedIdClass()
      {
           Name = "DidIReplaceYou"
      };
      session.Store(stringIdTest);
      session.SaveChanges();//This fails with a ConcurrencyViolation as I use OptimisticConcurrency and have Etag support on my objects
 }

所有东西都在使用文档存储的当前实例。这看起来很基本,所以一定是遗漏了一些简单的东西。

Raven为什么要为我的新文档生成一个已经存在的文档Id ?

好的,答案部分是Thomas所说的,但更具体地说,RavenDb完全忽略您手动插入的id。为了进行批量导入(比如从旧系统到新系统的数据迁移,就像我正在做的那样),您需要做以下操作:

  1. 确保连接到该数据库的任何文档存储都被处理掉(通常这意味着,不要让应用程序运行
  2. )
  3. 批量导入到系统中,自己设置id(你有责任确保你不覆盖任何现有的记录,但你可以使用乐观并发和标签)
  4. 在导入结束时,使用以下代码将您的集合的hilo(基本上是新id的起始点)设置为高于系统中当前最高id的值:

    session.Store(new {Id = "Raven/Hilo/{yourcollectionname}", Max = newMaxValue});
    session.SaveChanges(); 
    
  5. 现在当你初始化一个新的存储将使用正确的hilo值

注意,这个过程是不可能用内存版本的EmbeddableDocumentStore来模拟的,因为每次你打开一个商店时,你都要创建一个全新的服务器实例。因此,如果您试图假设已知的现有/不存在记录的id进行集成测试,您将不得不让Raven选择id,而不是在您自己的对象中设置它们。这应该工作得很好,因为你的新内存存储总是从1开始。为了确保测试的隔离性,我为每个测试都开设了一个新商店。

RavenDB的HiLo密钥生成器不知道StringBasedIdClasses/1已经在使用中,因此它将其分配给您的第二个文档。