在RavenDB中作为Id的Guid

时间:2012-10-01 23:32:27

标签: ravendb

RavenDb documentation州:

  

支持Numeric或Guid Id属性,可以无缝工作。在这种情况下,RavenDB将自动将内部字符串ID转换为实体中显示的数字或Guid值,然后返回。

我存储了以下对象:

class A
{
    public Guid Id { get; set; }
    public Guid BId { get; set; }
}

class B
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

然后我创建了以下投影:

class AB
{
    public Guid Id { get; set; }       // This should be the Id of A
    public Guid BId { get; set; }      // This should be the Id of B
    public string BName { get; set; }  // This should be the name of B
}

我创建了以下索引来创建投影:

class MyIndex : AbstractIndexCreationTask<AB>
{
    public MyIndex()
    {
        Map = docs =>
            from d in docs
            select new
            {
                d.Id,
                d.BId,
                BName = string.Empty
            };

        TransformResults = (database, results) =>
            from r in results
            let b = database.Load<B>("bs/" + r.BId.ToString())
            select new
            {
                r.Id,
                r.BId,
                BName = b.Name
            };
    }
}

当我使用以下查询时:

session.Query<AB, MyIndex>().FirstOrDefault(t => t.Id == guid);

我得到了这个例外:

  

转换值时出错&#34; bs / cc0a65ae-dd36-4437-8a57-fa20b91eeef7&#34;输入&#39; System.Guid&#39;。路径&#39; Id&#39;。

问题:

  1. 这是由我的投影中的转换引起的,因为Id是一个字符串而不是我的Guid。但是,将其删除将不会返回Id。我该怎么办?

  2. 我必须使用字符串构建"bs/" + r.BId.ToString()来加载相关文档。有没有办法不必这样做?是否有某种功能可以为我解析doc标签?

  3. 是否有完全去除文档标记的通用方法?

  4. 我的约束。

    我将生成Guid并且不能让RavenDb为我生成它。我知道文档ID实际上是字符串,但我真的需要使用我创建的Guid。我更愿意拥有我的实体的Id属性。

    我使用的是Raven.Client 1.0.972

5 个答案:

答案 0 :(得分:6)

您可以使用MultiMap / Reduce Index实现此目的,但您需要一些hackery:

1)你需要减少使用字符串,而不是guids。您仍然可以在AB类中将这些值作为guid返回,我将在下面进行演示。

2)你不能调用你的AB类“Id”的第一个属性,因为raven会尝试将其翻译为“__document_id”。所以称之为“AId”,它会正常工作。

3)在映射阶段,您必须自己操纵字符串以去除文档密钥前缀。

这是一个将所有内容组合在一起的示例程序。这表明它确实有效,但我认为它也说明了为什么Ayende更喜欢字符串标识符,所以你不必处理这种混乱。

using System;
using System.Linq;
using Raven.Client.Document;
using Raven.Client.Indexes;

namespace RavenScratchTest
{
  class Program
  {
    static void Main()
    {
      var documentStore = new DocumentStore { Url = "http://localhost:8080" };
      documentStore.Initialize();
      IndexCreation.CreateIndexes(typeof(Program).Assembly, documentStore);

      using (var session = documentStore.OpenSession())
      {
        var b = new B { Id = Guid.NewGuid(), Name = "Foo" };
        var a = new A { Id = Guid.NewGuid(), BId = b.Id };

        session.Store(a);
        session.Store(b);

        session.SaveChanges();
      }

      using (var session = documentStore.OpenSession())
      {
        var a = session.Query<A>().Customize(x => x.WaitForNonStaleResults()).First();
        var b = session.Query<B>().Customize(x => x.WaitForNonStaleResults()).First();

        Console.WriteLine("A:  Id = {0}", a.Id);
        Console.WriteLine("   BId = {0}", a.BId);
        Console.WriteLine();
        Console.WriteLine("B:  Id = {0}", b.Id);
        Console.WriteLine("  Name = {0}", b.Name);
        Console.WriteLine();

        var guid = a.Id;
        var ab = session.Query<AB, MyIndex>().Customize(x => x.WaitForNonStaleResults())
          .FirstOrDefault(t => t.AId == guid);

        if (ab == null)
          Console.WriteLine("AB: NULL");
        else
        {
          Console.WriteLine("AB:  AId = {0}", ab.AId);
          Console.WriteLine("   BId = {0}", ab.BId);
          Console.WriteLine("   BName = {0}", ab.BName);
          Console.WriteLine();
        }
      }

      Console.WriteLine();
      Console.WriteLine("Done.");
      Console.ReadLine();
    }
  }

  class A
  {
    public Guid Id { get; set; }
    public Guid BId { get; set; }
  }

  class B
  {
    public Guid Id { get; set; }
    public string Name { get; set; }
  }

  class AB
  {
    public Guid AId { get; set; }
    public Guid BId { get; set; }
    public string BName { get; set; }
  }

  class MyIndex : AbstractMultiMapIndexCreationTask<MyIndex.ReduceResult>
  {
    public MyIndex()
    {
      AddMap<A>(docs => from a in docs
                select new
                {
                  AId = a.Id.ToString().Split('/')[1],
                  a.BId,
                  BName = (string)null
                });

      AddMap<B>(docs => from b in docs
                select new
                {
                  AId = (string)null,
                  BId = b.Id.ToString().Split('/')[1],
                  BName = b.Name
                });

      Reduce = results => from result in results
                group result by result.BId
                into g
                select new
                  {
                    g.FirstOrDefault(x => x.AId != null).AId,
                    BId = g.Key,
                    g.FirstOrDefault(x => x.BName != null).BName
                  };
    }

    internal class ReduceResult
    {
      public string AId { get; set; }
      public string BId { get; set; }
      public string BName { get; set; }
    }
  }
}

答案 1 :(得分:2)

您可以在保存时明确向RavenDB提供ID:

session.Store(doc, explicitIdValueString);

explicitIdValueString可以是Guid字符串。此值将用于标识整个数据库中的文档,并且不会以类型标记名称为前缀。您还可以通过覆盖IDocumentStore.Conventions上的约定来自定义标记名称或ID生成策略,例如FindTypeTagName Func<Type, string>

答案 2 :(得分:2)

主要问题是虽然RavenDB可以处理客户端上的数字/整数,但是在服务器端,RavenDB使用字符串ID。

通常,不建议使用Guids /数字ID。

答案 3 :(得分:1)

假设您有用户并且您想为这些生成guid标识符。

new User { Id = "users/" + Guid.NewGuid().ToString("N") }

出于理智的目的,在文档中我急切地创建了密钥,我将它们设置为不可变的。

public class User
{
    public User(Guid? guid = null)
    {
        IdPart = (guid ?? Guid.NewGuid()).ToString("N")
    }

    string IdPart { get; }
    string Id => $"Users/{IdPart}"

有时,IdPart实际上是一把钥匙。假设我们有&#34; Users / abc&#34;。如果用户有项目。我将通常创建一个类似于:

的文档
public class Project
{
    public User(Guid? userId = null)
    {
        UserId = "Users/" + (guid ?? Guid.NewGuid()).ToString("N");
        Id = $"{UserId}/project/"

    }

请注意尾随project/这将通知raven在斜杠后创建HiLo值。

此概念可用于轻松混合指定的标识符,自然标识符和序列/ hilo /身份键,同时提升可读标识符而不是11是什么?但User/abc/project/1,我可以告诉你那是什么。 abc

创建的第一个项目

答案 4 :(得分:-1)

class MyIndex : AbstractIndexCreationTask<AB>
{
    public MyIndex()
    {
        Map = docs =>
            from d in docs
            select new
            {
                d.Id,
                d.BId,
                BName = string.Empty
            };

        TransformResults = (database, results) =>
            from r in results
            let b = database.Load<B>("bs/" + r.BId.ToString())
            select new
            {
                Id = Guid.Parse(r.Id.ToString().Split('/').Last()),
                r.BId,
                BName = b.Name
            };
    }
}