.net core下使用事件总线
事件总线,我的理解就是发布订阅模式,这里有一篇文章写的比较好,我就是按着这个文章来完成的事件总线:事件总线知多少。我之前按照他的文章结合自己写的,但是今天又看了下自己写的,发现好多都生疏了,所以觉得有必要来回忆下,这里只是我个人的理解,如有不对请指出。
1 2 3 4 5 6 7 | /// <summary> /// 领域命令基类(此处文章里我称之为事件源) /// </summary> public class Command { } |
然后我根据事件源来定义一个事件源处理的接口和它的实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /// <summary> /// 创建用户领域命令(创建事件源) /// </summary> public class CreateUserCommand: Command { public CreateUserCommand(User user) { User = user; } public User User { get ; private set ; } } /// <summary> /// 用户命令处理程序(处理事件源) /// </summary> public class UserCommandHandler : ICommandHandler<CreateUserCommand>, { private readonly IUserRepository _userRepository; private readonly IEventBus _eventBus; public UserCommandHandler(IUserRepository userRepository, IEventBus eventBus) { _userRepository = userRepository; _eventBus = eventBus; } public void Handler(CreateUserCommand command) { int count = _userRepository.SelectCountByAccount(command.User.Account); if (count > 0) { _eventBus.RaiseEvent( new NotifyValidation( "该账号已存在" )); return ; } _userRepository.Insert(command.User); } } |
此处我觉得已经完成了关于事件源的功能,那么我们就来思考根据这个事件源来处理发布订阅的关系。
就那注册用户功能来说,前面已经将注册用户的事件源已经写好了,那么发布订阅怎么处理呢?首先,我们应该清楚一个逻辑,那就是页面生成用户信息,后端获取信息生成UserModel,然后我们根据UserModel转为我们需要的CreateUserCommand,然后我们ICommandHandler根据CreateUserCommand来调用Handler,这是一个简单的调用逻辑。那么IcommandHnadler怎么知道调用哪一个Handler呢?那就是我将事件源和事件源处理类存入集合里面,这样我以后就会根据Command来获取到我的ICommandHandler了,又因为.net core遵循依赖注入原则,所以我需要往容器了注入ICommander和他的实现类,就是UserCommandhandler,这个时候可以说是已经将事件源都注册到了内存里了,以下是我的相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /// <summary> /// 临时存储类型数组 /// </summary> private static Type[] serviceTypes = Assembly.Load( "Blog.Domain" ).GetTypes(); private static ConcurrentDictionary<Type, IList<Type>> handlerMapping = new ConcurrentDictionary<Type, IList<Type>>(); public static IList<Type> GetOrAddHandlerMapping( this Type eventType) { return handlerMapping.GetOrAdd(eventType,(Type type)=> new List<Type>()); } /// <summary> /// 注册事件总线(事件源) /// </summary> /// <typeparam name="TImplementation">ICommandler<CreateUserCommand></typeparam> /// <typeparam name="TService">CreateUserCommand</typeparam> /// <param name="serviceDescriptors"></param> public static void AddEventBus<TImplementation, TService>( this IServiceCollection serviceDescriptors) { Type handler = typeof (TImplementation); Type serviceType = serviceTypes.FirstOrDefault(s => handler.IsAssignableFrom(s)); //获得接口的实现类 if (serviceType == null ) throw new ArgumentNullException( string .Format( "类型{0}未找到实现类" , handler.FullName)); serviceDescriptors.AddTransient(handler, serviceType); //.net core自带的IOC容器 GetOrAddHandlerMapping( typeof (TService)).Add(handler); //将事件源和事件源处理程序注册到内存里,可以说生成了一个订阅列表 } |
接下来我们再看发布与订阅,我要先定义一个发布订阅的中间件,
1 2 3 4 5 6 7 8 9 10 11 12 | /// <summary> /// 中间件 /// </summary> public interface IEventBus { /// <summary> /// 发布 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> void Publish<TCommand>(TCommand command) where TCommand : Command; } |
然后还有它的实现类,处理逻辑就是根据UserCommandHandler去ConcurrentDictionary里找到它的对应的ICommandHandler,然后在从IOC容器找到ICommandHandler的实现类,然后执行里面的方法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public sealed class EventBus : IEventBus { private IServiceProvider _serviceProvider; public EventBus(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } /// <summary> /// 发布事件 /// </summary> /// <typeparam name="TEventData"></typeparam> /// <param name="eventData"></param> public void Publish<TCommand>(TCommand command) where TCommand : Command { IList<Type> types= typeof (TCommand).GetOrAddHandlerMapping(); if (types == null || types.Count == 0) throw new ServiceException( "事件总线未注册:" + typeof (TCommand).Name); foreach ( var type in types) //从订阅列表里寻找 { object obj = _serviceProvider.GetService(type); if (type.IsAssignableFrom(obj.GetType())) { ICommandHandler<TCommand> handler = obj as ICommandHandler<TCommand>; if (handler != null ) handler.Handler(command); // } } } } |
这个时候可以说已经完成了发布订阅,程序生成CreateUserCommand,这里我的理解为就是发布,调用Publish方法,这里我觉得就是订阅,然后最后执行Handler完成业务逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class UserService : IUserService { private readonly IUserRepository _userRepository; private readonly IEventBus _eventBus; /// <summary> /// 根据UserModel转实体 /// </summary> /// <param name="userModel"></param> /// <returns></returns> private User TransferModel(UserModel userModel) { return user; } public UserService(IUserRepository userRepository, IEventBus eventBus) { _userRepository = userRepository; _eventBus = eventBus; } public void Insert(UserModel userModel) { userModel.Password = EncrypUtil.MD5Encry(userModel.Password); var command = new CreateUserCommand(TransferModel(userModel)); //创建事件源 _eventBus.Publish(command); //发布命令 } } |
还有就是最后的IServiceCollection注入了:
1 2 3 4 5 6 7 8 9 10 11 | /// <summary> /// 服务集合 /// </summary> /// <param name="services"></param> public static void AddServices( this IServiceCollection services) { services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IUserService, UserService>(); services.AddEventBus<ICommandHandler<CreateUserCommand>, CreateUserCommand>();<br> services.AddTransient<IEventBus,EventBus>() services.DisposeServiceTypes(); } |
以上就是我对事件总线的具体应用,希望有大佬能指出我这菜鸟的不足指出!
好记性不如烂笔头,所以我把这个玩意用到了我的网站里面,我的个人站点的地址是:www.ttblog.site,源代码的地址是:https://github.com/Hansdas/BlogH.git,个人站点处于建设阶段,很多功能不完善,由于时间原因,所以进度比较慢,但是我也是每天回到家后都会去完善,自己做的饭再难吃也要吃完,自己做的网站,不好看也要用心呵护。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· 本地部署 DeepSeek:小白也能轻松搞定!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 基于DeepSeek R1 满血版大模型的个人知识库,回答都源自对你专属文件的深度学习。
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!