RavenDB起步--客户端API(二)

简介: 客户端API(二)

文档会话

会话是代码和 RavenDB 交互的主要方式。会话 API 中包含如下七个常用的高级 API :

  • Load()
  • Include()
  • Delete()
  • Query()
  • Store()
  • SaveChanges()
  • Advanced

下面我们对这七个 API 分别讲解。

Load()

我们使用 Load 可以将一个文档或多个文档加载到会话中,加载到会话中的文档由会话管理。一个文档只能在会话中加载一次。我们先来看一下代码:

var t1 = session.Load<ToDoTask>("ToDoTasks/1-A");
var t2 = session.Load<ToDoTask>("ToDoTasks/1-A");
Assert.True(Object.ReferenceEquals(t1, t2));

在上面的代码中虽然我们两次调用了 session.Load("ToDoTasks/1-A"); ,但是它只对 RavenDB 进行了一次查询,并且在会话中只有一个 ToDoTask 实例。每当我们加载文档的时候,都会首先检查会话管理内部的字典是否存在该文档,如果不存在就返回现有的实例,这样做有助于提高系统性能。

Load 可以一次加载多个文档,比如像下面这个代码那样,一次加载了三个文档:

Dictionary<string, ToDoTask> tasks = session.Load<ToDoTask>(
    "ToDoTasks/1-A",
    "ToDoTasks/2-A",
    "ToDoTasks/3-A"
);

在上面的代码中,将生成一个包含所有三个文档的字典,这三个文档是通过一次查询检索出来的。 如果在 RavenDB 中没有找到指定的文档,那么字典中文档的 ID 值为 null

这里需要说明的是,如果加载已经加载完成的文档,那么会话会从会话缓存中返回它们,如果文档不存在的话,会话也会记住无法加载该文档,并马上返回 null 不会再去尝试该文档。

Include()

在项目中我们大部分情况是在处理具有关联关系的文档,那么在 RavenDB 中我们该怎么处理呢?那么,着这一小节里我们来看看如何处理多文档。

首先更新我们的 Model ,在代码中添加 Person 实体类,并修改 ToDoTask 实体类:

public class Person
{
    public string Id { get; set; }
    public string Name { get; set; }
}
public class ToDoTask
{
    public string Id { get; set; }
    public string Task { get; set; }
    public bool Completed { get; set; }
    public DateTime DueDate { get; set; }
    public string AssignedTo { get; set; } 
    public string CreatedBy { get; set; } 
}

这两个实体类是相互独立,没有相互引用的, 这就说明我们可以获取单个文档以及使用单个文档,并且不需要加载其他文档。但是,我们在 ToDoTask 类中增加了 CreatedBy 和 AssignedTo 属性,这两个属性分别表示任务创建人和任务的执行人,他们的 Value 都是来自 Person 类中的 Id 字段。如果这时我们要在新增 Person 的同时给这个 Person 新增一个 ToDoTask 该怎么做呢?我相信有部分同学是这么想的:

using (var session = store.OpenSession())
{
    var person = new Person
    {
        Name = "Oscar Arava"
    };
    session.Store(person);
    session.SaveChanges();
    var task = new ToDoTask
    {
        DueDate = DateTime.Today.AddDays(1),
        Task = "Buy milk",
        AssignedTo = person.Id,
        CreatedBy = person.Id
    };
    session.Store(task);
    session.SaveChanges();
}

代码中执行了两次 SaveChanges 方法,这样看来似乎是没毛病。我前面的文章中也提到过 SaveChanges 方法会把前面所有的新增、修改、删除的内容一次性全部提交的 RavenDB 中,因此我们可以把第一个 SaveChanges 方法删掉。那么这时又有同学问了,我不保存 Person ,调用 person.Id 不就报错了吗?其实这个问题完全不必担心,当我们调用 session.Store(person) 后,RavenDB 客户端已经为 Perosn 的 Id 属性赋予了一个唯一值 ,因此在调用 person.Id 时不会出错。那么,现在我们知道了该如何保存多个文档了,下面我们就来看看如何将相关连的文档查询出来。

在 RavenDB 中其实是没有咱们常说的外键关系的,对另一个文档的引用只是一个字符串的属性。那么我们该如何查询出文档及其关联的文档呢?我相信,有的同学一定是这么想的:

using (var session = store.OpenSession())
{
    string taskId = Console.ReadLine();
    ToDoTask task = session.Load<ToDoTask>(taskId);
    Person assignedTo = session.Load<Person>(task.AssignedTo);
    Console.WriteLine(
        $"{task.Id} - {task.Task} by {assignedTo.Name}");
}

上面的代码虽然可以查出关联的数据,但是效率比较低,他执行了两次调用 RavenDB ,一次是获取 Task,另一次是获取 Poerson 。这个案例只是一个简单的查询,但是如果要查询复杂文档的话,这种多次调用就会严重影响效率和性能,那么如何解决呢?其实解决起来也很简单,我们可以使用 Include() 这个 API 。下面的代码就是修改过后的样子:

using (var session = store.OpenSession())
{
    string taskId = Console.ReadLine();
    ToDoTask task = session
              .Include<ToDoTask>(x => x.AssignedTo)
              .Load(taskId);
    Person assignedTo = session.Load<Person>(task.AssignedTo);
    Console.WriteLine(
      $"{task.Id} - {task.Task} by {assignedTo.Name}");
}

在这段代码中,我们在 Load 方法之前调用好了 Include 方法,这个方法告诉 RavenDB 当加载文档是,也应该同时根据 AssignedTo 属性去加载对应的 Person 文档。如果 AssignedTo 有值,那么就会和 ToDoTask 文档一起发送个客户端。这时,当我们调用 Load 方法来获取 Person 文档时,因为会话缓存中已经存在了这个文档,因此不会再去查询 RavenDB ,而是直接返回数据。在同一个操作中我们可以调用多次 Include() API,代码如下:

ToDoTask task = session.Include<ToDoTask>(x => x.AssignedTo)
                       .Include(x => x.CreatedBy)
                       .Load(taskId);

一上面这段代码为了,如果 AssignedTo 和 CreatedBy 都指向同一个文档的话,它只会返回一个文档副本,无论它被引用了多少次。

但是,这里要注意的是 Include 不能在被包含的文档中查询引用的文档,也就是说我们可以通过 ToDoTask 文档查询对应的 Person 文档,但是不能通过 Person 文档查询出是哪些 ToDoTask 文档引用了它,具体原理我将在后续的专题中讲解。

目录
相关文章
|
4月前
|
存储 API Apache
【zookeeper 第三篇章】客户端 API
本文介绍了Apache ZooKeeper客户端的一些常用命令及其用法。首先,`create`命令用于创建不同类型的节点并为其赋值,如持久化节点、有序节点及临时节点等。通过示例展示了如何创建这些节点,并演示了创建过程中的输出结果。其次,`ls`命令用于列出指定路径下的所有子节点。接着,`set`命令用于更新节点中的数据,可以指定版本号实现乐观锁机制。
35 0
|
3月前
|
负载均衡 API 数据安全/隐私保护
Zookeeper的客户端-原生的API
Zookeeper的客户端-原生的API
|
4月前
|
安全 API 网络安全
【Azure API 管理】APIM如何配置客户端证书的CRL检测策略
【Azure API 管理】APIM如何配置客户端证书的CRL检测策略
|
4月前
|
测试技术 编译器 Go
依赖注入与控制反转:优化Go语言REST API客户端
依赖注入与控制反转:优化Go语言REST API客户端
|
4月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
172 0
|
4月前
|
API
【Azure 应用服务】在App Service中调用外部服务API时需要携带客户端证书,而多次调用的情况下会出现WindowsCryptographicException Keyset does not exist异常
【Azure 应用服务】在App Service中调用外部服务API时需要携带客户端证书,而多次调用的情况下会出现WindowsCryptographicException Keyset does not exist异常
|
4月前
|
存储 安全 API
【Azure API 管理】在APIM中使用客户端证书验证API的请求,但是一直提示错误"No client certificate received."
【Azure API 管理】在APIM中使用客户端证书验证API的请求,但是一直提示错误"No client certificate received."
|
16天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
28天前
|
供应链 数据挖掘 API
电商API接口介绍——sku接口概述
商品SKU(Stock Keeping Unit)接口是电商API接口中的一种,专门用于获取商品的SKU信息。SKU是库存量单位,用于区分同一商品的不同规格、颜色、尺寸等属性。通过商品SKU接口,开发者可以获取商品的SKU列表、SKU属性、库存数量等详细信息。
|
29天前
|
JSON API 数据格式
店铺所有商品列表接口json数据格式示例(API接口)
当然,以下是一个示例的JSON数据格式,用于表示一个店铺所有商品列表的API接口响应