在RavenDB中作为Id的Guid

问题描述 投票:5回答:5

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

我得到这个例外:

将值“bs / cc0a65ae-dd36-4437-8a57-fa20b91eeef7”转换为“System.Guid”类型时出错。路径'Id'。

问题:

  1. 它是由我的投影中的转换引起的,因为Id是一个字符串而不是我的Guid。但是,将其删除将不会返回Id。我必须做什么?
  2. 我必须使用字符串构建"bs/" + r.BId.ToString()来加载相关的文档。有没有办法不必这样做?是否有某种功能可以为我解析doc标签?
  3. 是否有一种通用的方法来完全删除文档标记?

我的约束。

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

我正在使用Raven.Client 1.0.972

ravendb
5个回答
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; }
    }
  }
}

2
投票

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

session.Store(doc, explicitIdValueString);

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


2
投票

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

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


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实际上是一把钥匙。假设我们有“Users / abc”。如果用户有项目。我将通常创建一个类似于的文档:

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创建的第一个项目


-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
            };
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.