0Pricing
C# Academy · 课时

通知与事件

使用 MediatR 发布领域事件。

通知与事件 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

请求与通知

请求只有一个 handler,并且会返回一个值。通知是一种广播:它有零个或多个 handler,并且不返回任何内容。请使用通知来宣布某件事情已经发生。

// Request:      one handler, returns a result
// Notification: many handlers, fire-and-forget

INotification

领域事件是实现 INotification 的记录。它描述的是过去发生的事实。

public record OrderPlacedNotification(int OrderId, int CustomerId)
    : INotification;

通知处理器

每个订阅者都会实现 INotificationHandler<TNotification>。多个处理器可以订阅同一个通知,并且彼此独立地处理该通知。

public class SendConfirmationEmailHandler
    : INotificationHandler<OrderPlacedNotification>
{
    public Task Handle(
        OrderPlacedNotification n, CancellationToken ct)
    {
        // send the email...
        return Task.CompletedTask;
    }
}

多个订阅者

您可以根据需要添加任意数量的处理器。它们互不了解,因此无需修改发布者即可添加新的响应。

public class UpdateInventoryHandler
    : INotificationHandler<OrderPlacedNotification>
{
    public Task Handle(OrderPlacedNotification n, CancellationToken ct)
    {
        // decrement stock...
        return Task.CompletedTask;
    }
}

发布

若要发出通知,请注入 IPublisher(或 IMediator),然后调用 Publish。每个已注册的处理器都会被调用。

public class CreateOrderHandler(AppDbContext db, IPublisher publisher)
    : IRequestHandler<CreateOrderCommand, int>
{
    public async Task<int> Handle(CreateOrderCommand c, CancellationToken ct)
    {
        var order = new Order { CustomerId = c.CustomerId };
        db.Orders.Add(order);
        await db.SaveChangesAsync(ct);
        await publisher.Publish(
            new OrderPlacedNotification(order.Id, c.CustomerId), ct);
        return order.Id;
    }
}

解耦副作用

通知可以让主要用例保持专注。创建 order 时只需记录订单并发布事件;发送电子邮件和更新库存则分别放在各自的处理器中。这正是开闭原则的实际应用。

// CreateOrderHandler does NOT know about email or inventory

发布策略

默认情况下,MediatR 会按顺序等待每个通知处理器。如果某个处理器抛出异常,后续处理器将不会运行。您可以提供自定义 publisher,让它们并行运行。

builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.NotificationPublisher = new TaskWhenAllPublisher();
});

处理失败

由于各处理器彼此独立,因此请明确失败的含义。对于非关键的副作用,请在 handler 内捕获异常并记录日志,以免一个失败影响其他处理器。

public async Task Handle(OrderPlacedNotification n, CancellationToken ct)
{
    try { await _email.SendAsync(n.CustomerId, ct); }
    catch (Exception ex) { _logger.LogError(ex, "email failed"); }
}

领域事件模式

一种常见模式是:实体收集领域事件,在事务保存完成后统一发布这些事件。这样可以确保只有在更改提交后才触发事件。

await db.SaveChangesAsync(ct);
foreach (var domainEvent in order.Events)
    await publisher.Publish(domainEvent, ct);
order.Events.Clear();

通知与消息队列

MediatR 通知在进程内运行,并且在请求范围内是同步的。如果需要跨服务传递或持久化投递,请改为发布到真正的消息代理(例如 RabbitMQ、Azure Service Bus)。

// In-process reactions  -> MediatR notifications
// Cross-service / durable -> message broker

测试通知

您可以传入模拟的 IPublisher,并断言其已被调用,从而验证某个处理器是否发布了正确的事件。

var publisher = new Mock<IPublisher>();
var handler = new CreateOrderHandler(db, publisher.Object);
await handler.Handle(new CreateOrderCommand(1, []), default);
publisher.Verify(p => p.Publish(
    It.IsAny<OrderPlacedNotification>(), default), Times.Once);

快速检查

确认您对通知模型的理解。

回顾

您已经学习了通知和事件:

  • INotification 是一种广播,可以有零个或多个 INotificationHandler。
  • 使用 IPublisher.Publish 发布通知,以调用所有订阅者。
  • 通知可以解耦副作用,并遵循开闭原则。
  • 通知在进程内运行;对于跨服务或需要持久化的事件,请使用消息代理。

CQRS 和 MediatR 课程到此完成。

常见问题解答

「通知与事件」课时是免费的吗?

是的 — 「通知与事件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「通知与事件」这节课中我会学到什么?

使用 MediatR 发布领域事件。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「通知与事件」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. CQRS 概念
  2. 使用 MediatR 处理命令和请求
  3. 管道行为
  4. 通知与事件
← 返回 C# Academy