开发者中心 » 编程语言 » C# » 代码指南 » 使用 .NET 进行托管事务工作流

.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_ratingratings_count
  • 消耗结果,以便驱动程序完成查询元数据和事务状态的处理

只有整个事务成功,两个查询才会同时成功完成。

摘要

本示例演示了如何:

  • 在单个原子事务中管理多步骤工作流
  • 安全地计算派生值(聚合值)并将其写回
  • 使用 Neo4j .NET 驱动程序的异步会话和事务 API
  • 安全地传递参数并检索强类型值

分享文章