首页 > 其他分享 >在 .NET 中深入了解事件总线的使用与实现

在 .NET 中深入了解事件总线的使用与实现

时间:2024-11-02 15:44:49浏览次数:4  
标签:Task handlers 总线 handler 深入 事件 NET public

引言

在现代软件架构中,尤其是微服务和事件驱动设计中,事件总线(Event Bus)是实现组件间解耦和异步通信的重要工具。事件总线通过允许不同组件之间以松耦合的方式进行交互,从而提升了系统的灵活性和可维护性。本文将详细探讨在 .NET 中实现事件总线的方式,包括其工作原理、使用方法以及完整的示例代码。

什么是事件总线?

事件总线是一种中介,负责在事件生产者(Publisher)和事件消费者(Subscriber)之间传递事件。它允许系统中的各个组件通过事件进行通信,降低了组件之间的直接依赖。

事件总线的关键概念

  1. 事件(Event):表示某个关键业务行为或状态变化的对象,通常携带相关的数据。

  2. 事件生产者(Publisher):发布事件的组件或服务。

  3. 事件消费者(Subscriber):对特定事件感兴趣并响应的组件或服务。

  4. 事件处理程序(Event Handler):实现事件处理逻辑的类。

事件总线的优点

  • 解耦:事件生产者和消费者之间没有直接的依赖关系,使得组件可以独立开发和测试。

  • 灵活性:可以在运行时动态添加或移除事件处理程序。

  • 异步处理:支持异步事件处理,提升系统的响应能力。

在 .NET 8 中实现事件总线

1. 定义事件

首先,我们需要定义事件类,该类包含必要的属性来描述事件的内容。

public class OrderCreatedEvent
{
   public int OrderId { get; }
   public string CustomerName { get; }
   public DateTime CreatedAt { get; }

   public OrderCreatedEvent(int orderId, string customerName)
  {
       OrderId = orderId;
       CustomerName = customerName;
       CreatedAt = DateTime.UtcNow;
  }
}

2. 定义事件处理程序

接下来,定义一个事件处理程序接口和一个具体实现,用于处理事件逻辑。

public interface IEventHandler<T>
{
   Task Handle(T eventMessage);
}

public class OrderCreatedEventHandler : IEventHandler<OrderCreatedEvent>
{
   public Task Handle(OrderCreatedEvent eventMessage)
  {
       Console.WriteLine($"Order created: {eventMessage.OrderId} for {eventMessage.CustomerName} at {eventMessage.CreatedAt}");
       return Task.CompletedTask;
  }
}

3. 创建事件总线

然后,创建一个事件总线类,负责管理事件的发布和订阅关系。

public interface IEventBus
{
   void Subscribe<T>(IEventHandler<T> handler);
   void Unsubscribe<T>(IEventHandler<T> handler);
   Task Publish<T>(T eventMessage);
}

public class EventBus : IEventBus
{
   private readonly Dictionary<Type, List<object>> _handlers = new();

   public void Subscribe<T>(IEventHandler<T> handler)
  {
       if (!_handlers.ContainsKey(typeof(T)))
      {
           _handlers[typeof(T)] = new List<object>();
      }
       _handlers[typeof(T)].Add(handler);
  }

   public void Unsubscribe<T>(IEventHandler<T> handler)
  {
       if (_handlers.ContainsKey(typeof(T)))
      {
           _handlers[typeof(T)].Remove(handler);
      }
  }

   public async Task Publish<T>(T eventMessage)
  {
       if (_handlers.ContainsKey(typeof(T)))
      {
           var tasks = _handlers[typeof(T)].Cast<IEventHandler<T>>()
              .Select(handler => handler.Handle(eventMessage));
           await Task.WhenAll(tasks);
      }
  }
}

4. 配置依赖注入

在 .NET 8 应用中,我们可以利用依赖注入将事件总线和事件处理程序注册到服务容器中。

public void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<IEventBus, EventBus>();
   services.AddTransient<IEventHandler<OrderCreatedEvent>, OrderCreatedEventHandler>();
}

5. 使用事件总线

在业务逻辑中,我们可以使用事件总线来发布事件。以下是一个示例,展示如何在服务中使用事件总线。

public class OrderService
{
   private readonly IEventBus _eventBus;

   public OrderService(IEventBus eventBus)
  {
       _eventBus = eventBus;
  }

   public async Task CreateOrder(int orderId, string customerName)
  {
       var orderCreatedEvent = new OrderCreatedEvent(orderId, customerName);
       await _eventBus.Publish(orderCreatedEvent);
  }
}

6. 在主程序中触发事件

最后,我们可以在主程序中创建 OrderService 的实例并调用创建订单的方法,从而触发事件。

public class Program
{
   public static async Task Main(string[] args)
  {
       var serviceProvider = new ServiceCollection()
          .AddSingleton<IEventBus, EventBus>()
          .AddTransient<IEventHandler<OrderCreatedEvent>, OrderCreatedEventHandler>()
          .AddTransient<OrderService>()
          .BuildServiceProvider();

       var orderService = serviceProvider.GetRequiredService<OrderService>();
       await orderService.CreateOrder(1, "John Doe");
  }
}

示例应用

完整代码示例

下面是一个完整的 .NET 控制台应用程序示例,展示了事件总线的实现和使用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace EventBusExample
{
   public class OrderCreatedEvent
  {
       public int OrderId { get; }
       public string CustomerName { get; }
       public DateTime CreatedAt { get; }

       public OrderCreatedEvent(int orderId, string customerName)
      {
           OrderId = orderId;
           CustomerName = customerName;
           CreatedAt = DateTime.UtcNow;
      }
  }

   public interface IEventHandler<T>
  {
       Task Handle(T eventMessage);
  }

   public class OrderCreatedEventHandler : IEventHandler<OrderCreatedEvent>
  {
       public Task Handle(OrderCreatedEvent eventMessage)
      {
           Console.WriteLine($"Order created: {eventMessage.OrderId} for {eventMessage.CustomerName} at {eventMessage.CreatedAt}");
           return Task.CompletedTask;
      }
  }

   public interface IEventBus
  {
       void Subscribe<T>(IEventHandler<T> handler);
       void Unsubscribe<T>(IEventHandler<T> handler);
       Task Publish<T>(T eventMessage);
  }

   public class EventBus : IEventBus
  {
       private readonly Dictionary<Type, List<object>> _handlers = new();

       public void Subscribe<T>(IEventHandler<T> handler)
      {
           if (!_handlers.ContainsKey(typeof(T)))
          {
               _handlers[typeof(T)] = new List<object>();
          }
           _handlers[typeof(T)].Add(handler);
      }

       public void Unsubscribe<T>(IEventHandler<T> handler)
      {
           if (_handlers.ContainsKey(typeof(T)))
          {
               _handlers[typeof(T)].Remove(handler);
          }
      }

       public async Task Publish<T>(T eventMessage)
      {
           if (_handlers.ContainsKey(typeof(T)))
          {
               var tasks = _handlers[typeof(T)].Cast<IEventHandler<T>>()
                  .Select(handler => handler.Handle(eventMessage));
               await Task.WhenAll(tasks);
          }
      }
  }

   public class OrderService
  {
       private readonly IEventBus _eventBus;

       public OrderService(IEventBus eventBus)
      {
           _eventBus = eventBus;
      }

       public async Task CreateOrder(int orderId, string customerName)
      {
           var orderCreatedEvent = new OrderCreatedEvent(orderId, customerName);
           await _eventBus.Publish(orderCreatedEvent);
      }
  }

   public class Program
  {
       public static async Task Main(string[] args)
      {
           var serviceProvider = new ServiceCollection()
              .AddSingleton<IEventBus, EventBus>()
              .AddTransient<IEventHandler<OrderCreatedEvent>, OrderCreatedEventHandler>()
              .AddTransient<OrderService>()
              .BuildServiceProvider();

           var orderService = serviceProvider.GetRequiredService<OrderService>();
           await orderService.CreateOrder(1, "John Doe");
      }
  }
}

运行示例

将上述代码复制到新的 .NET 8 控制台应用程序中,运行后,你将看到控制台输出类似于以下内容:

Order created: 1 for John Doe at 2024-11-02T12:34:56.789Z

总结

事件总线是构建解耦和高效系统的重要组成部分。在 .NET 8 中,通过依赖注入可以轻松实现事件总线的功能。本文详细介绍了事件总线的基本概念、实现步骤和使用方法,希望能够帮助开发者在自己的项目中有效地应用事件总线模式。通过这种方式,您可以构建出更具灵活性和可维护性的应用程序。



标签:Task,handlers,总线,handler,深入,事件,NET,public
From: https://www.cnblogs.com/forges/p/18521997

相关文章

  • .NET 8 中 Entity Framework Core 的使用
    本文代码:https://download.csdn.net/download/hefeng_aspnet/89935738 概述        EntityFrameworkCore(EFCore)已成为.NET开发中数据访问的基石工具,为开发人员提供了强大而多功能的解决方案。随着.NET8和C#10中引入的改进,开发人员现在可以使用更丰......
  • Windows 基础(一):深入理解Windows,掌握命令行与Shell
    内容预览≧∀≦ゞWindows基础(一)声明导语一、Windows和Linux的区别二、Windows的ShellShell和终端的区别1.命令提示符(CMD)2.WindowsPowerShell3.WindowsTerminal4.WindowsSubsystemforLinux(WSL)三、Windows常用命令1.文件与目录操作2.文本处理3.......
  • .NET开源的实时应用监控系统 - WatchDog
    项目介绍WatchDog是一个开源(MITLicense)、免费、针对ASP.NetCoreWeb应用程序和API的实时应用监控系统。开发者可以实时记录和查看他们的应用程序中的消息、事件、HTTP请求和响应,以及运行时捕获的异常。项目工作原理它利用SignalR进行实时监控,并使用LiteDB作为无需配置的类似Mongo......
  • 深入学习指针!指针史上最全解析!!(1)
    文章目录1.内存和地址1.1内存1.2究竟该如何理解编址2.指针变量和地址2.1取地址操作符(&)2.2指针变量和解引用操作符(*)2.2.1指针变量2.2.2如何拆解指针类型2.2.3解引用操作符2.3指针变量的大小3.指针变量类型的意义3.1指针的解引用3.2指针+-整数3.3void*指针4.指针运......
  • C#/.NET/.NET Core技术前沿周刊 | 第 11 期(2024年10.21-10.31)
    前言C#/.NET/.NETCore技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NETCore领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。欢迎投稿、推荐或自荐优质文章、项目、学习资源等。......
  • 制作一个龙芯旧世界的 dotnet sdk docker 镜像
    以下是我的dockerfile文件,内容特别简单FROMcr.loongnix.cn/library/debian:busterWORKDIR/rootRUNapt-getupdate-y&&\apt-getinstall-y--no-install-recommends\apt-transport-https\ca-certificates\curl\git......
  • systemctl restart NetworkManager 重启后,文件/etc/resolv.conf修改失败
    如果你在重启NetworkManager之后发现无法修改/etc/resolv.conf文件,这是因为NetworkManager会自动管理这个文件为了解决这个问题,你可以采取以下两种方法之一:方法一:禁用NetworkManager服务使用以下命令停止NetworkManager服务:sudosystemctlstopNetworkMana......
  • 4.step into netty
    1.NIO现存的问题1.1客户端中断导致死循环详情在3.网络多路复用通信模型中1.2粘包/拆包问题可能P1和P2被合在一起发送给了服务端(粘包现象)可能P1和P2的前半部分合在一起发送给了服务端(拆包现象)可能P1的前半部分就被单独作为一个部分发给了服务端,后面的和P2一起发给服务端......
  • 深入解析 Transformers 框架(三):Qwen2.5 大模型的 AutoTokenizer 技术细节
    前面2篇文章,我们通过查看Transformers包代码,学习了Transformer包模块API设计、模型初始化和加载流程:第1篇:transformers推理Qwen2.5等大模型技术细节详解(一)transformers包和对象加载第2篇:transformers推理Qwen2.5等大模型技术细节详解(二)AutoModel初始化......
  • 深入解析 FastAPI 查询参数:配置、类型转换与灵活组合
    深入解析FastAPI查询参数:配置、类型转换与灵活组合本文全面解析了FastAPI查询参数的使用方法,包括配置默认值、设为可选或必选参数、类型转换以及组合使用等实用技巧。通过查询参数,开发者可以在路径操作函数中接收动态输入,灵活地构建API接口。文章详细说明了如何利用......