开发者中心 » 编程语言 » C# » 教程 » 使用 .NET 快速上手 Neo4j

使用 .NET 快速上手 Neo4j

欢迎阅读使用 .NET 应用程序连接 Neo4j 的入门指南! 

在本教程中,我们将逐步介绍如何开始运行以及如何使用 Neo4j .NET 驱动程序查询 Neo4j 数据库。重点在于开始使用 .NET 驱动程序,而不是深入探讨 Neo4j 查询语言 Cypher 的奇妙世界。

设置数据库实例

为了尽可能简单,我们将使用由 Neo4j 托管的便捷演示数据库。 

在浏览器中打开 https://demo.neo4jlabs.com:7473/browser/

然后使用以下凭据连接到实例:

现在,你将进入 Neo4j 浏览器界面。你可以通过在命令行提示符中运行以下 Cypher 语句来验证数据集是否正常运行。

goodreads$ CALL db.schema.visualization

你应该能看到返回的数据模型可视化图形。

请注意,此数据库为只读,因此适合我们在此处的演示目的。当你想开始添加和更新图形数据时,可以利用自己的免费 Neo4j Aura 云实例:https://console.neo4j.io/。或者,如果你希望在本地机器上运行,请从 /deployment-center/ 下载 Neo4j Desktop。 

现在,让我们开始 .NET 开发之旅。

要求

开发用于连接 Neo4j 的 .NET 应用程序有两个要求:

  • 计算机上已安装 .NET 版本。为方便起见,我们建议使用最新版本。
  • 项目中已安装最新版本的 Neo4j .NET 驱动程序包作为依赖项。

用于 .NET 开发的集成开发环境 (IDE) 有很多,其中大多数都具备创建和配置项目的功能。请随意使用你喜欢的 IDE,无论是 Visual Studio Code、Rider 还是其他工具。 

设置项目

为了使本教程不依赖于特定的 IDE,我们将使用命令行来完成设置。此示例是在 Mac 的终端中完成的,因此具体命令可能因操作系统而异。

首先,我们将为控制台应用程序创建目录和项目。导航到你想要存放项目的目录(在本例中为我的 Git 目录),然后运行以下命令:

Me@My-MacBook-Pro Git % dotnet new console --output Neo4jTutorialCode language: Shell Session (shell)

你将看到以下输出:

The template "Console App" was created successfully.
Processing post-creation actions...
Restoring /Users/andrewheap/Git/Neo4jTutorial/Neo4jTutorial.csproj:
  Determining projects to restore...
  Restored /Users/andrewheap/Git/Neo4jTutorial/Neo4jTutorial.csproj (in 23 ms).
Restore succeeded.Code language: Shell Session (shell)

导航到你的项目目录:

Me@My-MacBook-Pro git % cd Neo4jTutorialCode language: Shell Session (shell)

如果你运行 ‘ls’ 命令,将看到 ‘.csproj’ 文件、Program.cs 文件和一个 obj 目录。

Me@My-MacBook-Pro Neo4jTutorial % ls
Neo4jTutorial.csproj	obj			Program.csCode language: Shell Session (shell)

现在,将最新的 Neo4j 驱动程序包添加到项目中:

Me@My-MacBook-Pro Neo4jTutorial % dotnet add package Neo4j.DriverCode language: Shell Session (shell)

此命令会向 Neo4jTutorial.csproj 文件添加一个条目,并下载必要的依赖项。

编写第一段代码

从这里开始,只需很少的代码即可让驱动程序从数据库提取结果。以下代码片段是所需的最低配置。

using Neo4j.Driver;

var driverInstance = GraphDatabase.Driver(new Uri("neo4j+s://demo.neo4jlabs.com"),
AuthTokens.Basic("goodreads", "goodreads")); ①

string queryString = @"Match(b:Book)-[:AUTHORED]-(a:Author {name:'Stephen King'}) 
RETURN b.title AS Title"; ②

var records = await driverInstance.ExecutableQuery(queryString).ExecuteAsync(); ③

这段代码在做什么? 

  1. 在这里创建了驱动程序对象实例,并为其提供了 URI 和一些基本的身份验证详细信息。
  2. 这里有一个非常简单的 Cypher 查询字符串,它将查找所有由斯蒂芬·金 (Stephen King) 编写的书籍。
  3. 这一行是执行查询的地方,结果将作为记录返回。使用 ExecutableQuery API 时,结果会以 EagerResult 的形式返回(详见:驱动程序 API 参考文档)。 

让我们进行一些扩展

在上面的代码示例中,我们编写了保持代码简洁且易于理解所需的最少行数。但是,你还应该了解并整合进代码中的几个最佳实践和实用元素。

using Neo4j.Driver;
using Neo4j.Driver.Mapping;

//Create a driver instance supplying where to connect to and the authentication details
var driverInstance = GraphDatabase.Driver(new Uri("neo4j+s://demo.neo4jlabs.com"),
AuthTokens.Basic("goodreads", "goodreads"));

string queryString = @"MATCH (r:Review)-[:WRITTEN_FOR]->(b:Book)<-[:AUTHORED]-(a:Author)
              	WHERE a.name = $name
               RETURN b.title AS Title, r.text AS ReviewText, r.rating As Rating
               ORDER BY r.rating DESC
            	LIMIT 20"; ①

//Run the Cypher query, supplying it with a target database, required parameters, and //receive back the results mapped to your application specific type.
var bookReviews = await driverInstance
.ExecutableQuery(queryString)
    	.WithConfig(new QueryConfig(database: "goodreads")) ②
    	.WithParameters(new {name = "Stephen King"}) ③
    	.ExecuteAsync()
    	.AsObjectsAsync<BookReview>(); ④

//Do something with the results
bookReviews.ToList().ForEach(br => Console.WriteLine($"Title = {br.Title}")); ⑤

//Domain type declaration
record BookReview(string Title, string ReviewText, int Rating); ⑥
  1. 这里有一个稍微复杂的查询。需要注意的重要一点是,我们对其进行了参数化。务必使用查询参数,而不是将值硬编码或拼接进查询字符串中。这可以防止 Cypher 注入,并能更好地利用数据库查询缓存。
  2. 这一行提供了执行查询的配置。通过它,我们可以控制查询运行的多个方面。在本例中,它指定了要查询的数据库名称。这是一个最佳实践,因为它可以避免所谓的“主数据库解析 (home database resolution)”,这会额外产生一次到服务器的往返,从而导致性能损失。
  3. 这提供了该查询正在使用的参数。
  4. 这是一个名为“对象映射”的酷炫功能的示例。驱动程序可以自动获取接收到的记录,并将它们映射到你声明和提供的对象类型实例中。在本例中,对于接收到的每条记录,它都会创建并填充一个 BookReview 对象。这是一个高度可配置且灵活的系统,值得深入探索。
  5. 在这个例子中,我们还添加了一些输出。如果我们不对数据进行处理,那这一切还有什么意义呢?!
  6. 最后一行声明了用于映射接收记录的自定义领域类型。

重试机制与重要说明

我们在这里需要提到驱动程序 ExecutableQuery 接口中内置的一项重要功能。 

在底层,此方法会执行重试。这意味着如果查询因瞬态错误(从连接失败到服务器端问题不等)而失败,驱动程序将会重试该查询。查询被封装在事务中以执行此操作,因此正常的回滚/提交语义适用。 

由于查询可能会被多次运行,因此它必须是幂等的。这意味着无论执行多少次,查询结果都应保持不变。例如,如果你要为节点属性生成一个值(比如递增计数器),且事务提交成功,但发生了连接错误,导致驱动程序没有收到提交成功的消息,那么系统会进行重试,计数器就会比预期大 1。这不是幂等的,因为结果可能会出现不同步。

摘要

在本教程中,我们首先介绍了 ExecutableQuery API,因为它旨在实现最快上手,同时还提供了丰富的功能并适用于许多用例。你使用 Neo4j 和 .NET 创建了一个入门应用程序,对数据库运行了 Cypher 查询并获得了结果,然后通过最佳实践增强了该应用程序。 

在你继续开发之旅时,编写生产环境的商业应用程序时有几个重要的细节需要牢记: 

  • ExecutableQuery 返回 EagerResult。顾名思义,这意味着所有结果记录在返回之前都会被立即获取并存储在内存中,如果结果集很大,这可能会对内存产生影响。 
  • 该 API 还具有多项附加功能,使你能够在从服务器收到每条记录时对其执行操作。例如 WithStreamProcessor(在收到每条记录时将其处理为新类型)和 WithFilter(允许根据你的要求在最终的 EagerResult 中排除或包含记录)。
  • 你可以为查询指定读或写路由。在 Neo4j 集群环境(如 Aura)中,存在领导者 (leader) 和跟随者 (follower) 服务器。写操作(即更新数据库的查询)将始终定向到领导者节点。读查询(不进行任何更新)可以路由到领导者或跟随者节点。默认设置为“写”,但如果你知道它是读查询,最好在配置中指定,以便在整个集群中获得更好的服务器利用率。

在另一篇教程中,我们将探讨驱动程序 API 中提供更多控制和灵活性(尽管增加了复杂性)的其他领域:会话 (session) 和事务 (transaction) 接口。

分享文章