.NET 托管事务工作流
此示例演示了如何使用 Neo4j .NET 驱动程序在托管写入事务中执行多步骤事务工作。代码展示了如何:
- 打开针对特定数据库的异步会话
- 运行第一个查询以计算新的聚合值(平均评分、评论总数)
- 运行第二个查询,使用计算出的聚合值更新同一个图书节点
- 在单个原子事务中执行这两个操作
- 使用驱动程序的映射辅助工具安全地提取结果并处理类型化值

代码示例
以下是完整代码,随后将详细介绍如何在单个事务中读取数据、计算聚合值并将更新后的值写回 Neo4j。
await using var session = driver
.AsyncSession(conf => conf.WithDatabase("goodreads"));
await session.ExecuteWriteAsync(async tx =>
{
// Query 1: Compute fresh aggregates from reviews
var result1 = await tx
.RunAsync(readCypher, new { title = "The Tommyknockers" })
.ConfigureAwait(false);
var record = await result1.SingleAsync();
var avgRating = record["avgRating"].As<double>();
var ratingsCount = record["ratingsCount"].As<long>();
var bookId = record["bookId"].As<string>();
// Query 2: Update the book with recomputed values
var result2 = await tx
.RunAsync(writeCypher,
new { bookId, avgRating, ratingsCount })
.ConfiguerAwait(false);
var resultSummary = await result2.ConsumeAsync().ConfigureAwait(false);
})
.ConfigureAwait(false);Code language: C++ (cpp)
使用 .NET 驱动程序更新评分
1. 定义 Cypher 查询
string readCypher = @"MATCH (b:Book {title:$title)<-[:WRITTEN_FOR]-(r:Review)
RETURN b.book_id AS bookId, avg(r.rating)
AS avgRating, count(r) AS ratingsCount";
string writeCypher = @"MATCH (b:Book {book_id: $bookId})
SET b.average_rating = $avgRating,
b.ratings_count = $ratingsCount
RETURN b"Code language: C# (cs)
查询 1 (readCypher):
- 按书名查找图书
- 检索所有关联的评论 (Review) 节点
- 返回
bookId(更新步骤所需)- 计算出的
avgRating - 计算出的
ratingsCount
查询 2 (writeCypher):
- 通过 ID 定位图书
- 更新节点属性:
- 新的
average_rating - 新的
ratings_count
- 新的
- 返回更新后的图书
这两个查询旨在单个事务内运行,因此它们基于一致的数据进行操作。
2. 打开异步会话
await using var session = driver
.AsyncSession(conf => conf.WithDatabase("goodreads"));Code language: C# (cs)
- 创建一个轻量级、非线程安全的会话
- 目标是
goodreads数据库 - 会话设计为短生命周期,仅在工作期间使用
3. 执行托管写入事务
await session.ExecuteWriteAsync(async tx =>
{
...
});Code language: C# (cs)
ExecuteWriteAsync确保所有包含的操作都在单个写入事务中运行- 如有必要,Neo4j 会自动重试事务(例如,处理瞬态错误)
4. 查询 1:计算最新聚合值
var result1 = await tx
.RunAsync(readCypher, new { title = "The Tommyknockers" })
.ConfigureAwait(false);
var record = await result1.SingleAsync();
var avgRating = record["avgRating"].As<double>();
var ratingsCount = record["ratingsCount"].As<long>();
var bookId = record["bookId"].As<string>();Code language: C# (cs)
执行过程:
RunAsync使用参数化的书名执行读取查询SingleAsync()检索预期的唯一一行数据- 每个值都使用类型安全的
.As<T>()转换进行提取
这为您提供了更新查询所需的新鲜数据。
5. 查询 2:更新图书节点
var result2 = await tx
.RunAsync(writeCypher,
new { bookId, avgRating, ratingsCount })
.ConfigureAwait(false);
var resultSummary = await result2
.ConsumeAsync()
.ConfigureAwait(false);Code language: C# (cs)
此步骤执行以下操作:
- 使用查询 1 中计算出的值运行更新查询
- 更新图书的
average_rating和ratings_count - 消耗结果,以便驱动程序完成查询元数据和事务状态的处理
只有整个事务成功,两个查询才会同时成功完成。
摘要
本示例演示了如何:
- 在单个原子事务中管理多步骤工作流
- 安全地计算派生值(聚合值)并将其写回
- 使用 Neo4j .NET 驱动程序的异步会话和事务 API
- 安全地传递参数并检索强类型值


