7

RavenDb文档指出:

支持数字或 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”时出错。路径“标识”。

问题:

  1. 这是由我的投影中的转换引起的,因为Id那里是一个字符串,而不是我的 Guid。但是,将其省略不会返回 Id。我必须做什么?

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

  3. 有没有一种通用的方法可以完全去除文档标签?

我的约束。

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

我正在使用 Raven.Client 1.0.972

4

5 回答 5

6

您可以使用 MultiMap/Reduce Index 来实现这一点,但您需要一些技巧:

1)您需要减少使用字符串,而不是指南。正如我将在下面演示的那样,您仍然可以在 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; }
    }
  }
}
于 2012-10-02T20:59:50.520 回答
2

您可以在保存时显式地向 RavenDB 提供一个 ID:

session.Store(doc, explicitIdValueString);

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

于 2012-10-02T00:00:30.903 回答
2

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

一般来说,不建议使用 Guids / numeric ids。

于 2012-10-02T08:32:38.967 回答
2

假设您有用户并且您想为这些用户生成 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 实际上是一个完整的键。假设我们有“用户/ abc”。如果用户有一个项目。我通常会创建一个类似于以下内容的文档:

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

    }

注意后面的project/this 将通知 raven 在斜线之后创建一个 HiLo 值。

此概念可用于轻松混合分配的标识符、自然标识符和序列/hilo/身份键,同时促进可读标识符而不是1. 1是什么?但是User/abc/project/1,我可以告诉你那是什么。abc创建的第一个项目

于 2016-08-09T14:05:47.950 回答
-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
            };
    }
}
于 2014-02-06T10:24:29.530 回答