直接上代码特征[AttributeUsage(AttributeTargets.Method,AllowMultipletrue,Inheritedfalse)]publicclassMqttTopicAttribute:Attribute{publicstringTopic{get;}/// summary/// 描述可选用于日志/文档/// /summarypublicstringDescription{get;set;};publicMqttTopicAttribute(stringtopic){if(string.IsNullOrWhiteSpace(topic))thrownewArgumentException(Topic 不能为空,nameof(topic));Topictopic;}}注册路由/// summary/// 路由注册/// /summarypublicclassMqttRouteRegistry{publicDictionarystring,List(Type ServiceType,MethodInfo Method)Routes{get;}new(StringComparer.OrdinalIgnoreCase);publicvoidScanAssembly(Assemblyassembly){foreach(vartypeinassembly.GetTypes()){varmethodstype.GetMethods(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);foreach(varmethodinmethods){//使用 GetCustomAttributes 获取方法上贴的所有 MqttTopic 特性varattributesmethod.GetCustomAttributesMqttTopicAttribute();foreach(varattributeinattributes){// 验证方法签名必须包含且仅包含一个 stringvarparametersmethod.GetParameters();// 校验参数类型是否为 MqttMessageContextif(parameters.Length!1||parameters[0].ParameterType!typeof(MqttMessageContext)){thrownewInvalidOperationException($[MqttTopic] 方法{type.Name}.{method.Name}必须有且仅有一个 MqttMessageContext 参数);}// 验证返回值if(method.ReturnType!typeof(Task)method.ReturnType!typeof(void)){thrownewInvalidOperationException($[MqttTopic] 方法{type.Name}.{method.Name}返回值必须是 void 或 Task);}// 初始化或追加到列表if(!Routes.TryGetValue(attribute.Topic,outvarhandlerList)){handlerListnewList(Type,MethodInfo)();Routes[attribute.Topic]handlerList;}handlerList.Add((type,method));}}}}}路由分发publicrecordMqttMessageContext(stringtopic,stringpayload);/// summary/// 路由分发/// /summarypublicclassMqttMessageDispatcher{privatereadonlyMqttRouteRegistry_registry;privatereadonlyIServiceProvider_serviceProvider;publicMqttMessageDispatcher(MqttRouteRegistryregistry,IServiceProviderserviceProvider){_registryregistry;_serviceProviderserviceProvider;}publicasyncTaskDispatchMessageAsync(stringtopic,stringpayload){// 1. 如果没有任何类订阅这个 Topic直接退出if(!_registry.Routes.TryGetValue(topic,outvarhandlers)||handlers.Count0){return;}// 2. 创建一个顶层的作用域或者为每个订阅者创建独立作用域。// 这里推荐采用“一个 Topic 创建一个 Scope”如果追求极致隔离也可以在 foreach 内部单独 Scope。using(varscope_serviceProvider.CreateScope()){vartasksnewListTask();// 3. 遍历所有订阅了该 Topic 的处理器实现一对多广播foreach(varhandlerinhandlers){// 使用 Task.Run 进行并行多线程分发防止订阅者 A 耗时导致订阅者 B 延迟收到消息tasks.Add(Task.Run(async(){try{// 动态向 DI 容器请求实例例如 MainWindowViewModel、AlarmService 等varinstancescope.ServiceProvider.GetService(handler.ServiceType);if(instancenull)return;varcontextnewMqttMessageContext(topic,payload);// 执行方法if(handler.Method.ReturnTypetypeof(Task)){await(Task)handler.Method.Invoke(instance,newobject[]{context});}else{handler.Method.Invoke(instance,newobject[]{context});}}catch(Exceptionex){// 单个订阅者执行崩溃捕获它不影响其他类接收消息System.Diagnostics.Debug.WriteLine($[Dispatcher] 订阅者{handler.ServiceType.Name}处理失败:{ex.Message});}}));}// 等待当前 Topic 的所有订阅者全部处理完毕awaitTask.WhenAll(tasks);}}}依赖注入// 1. 初始化并注册 MQTT 路由注册表,所有消息处理的类必须注入varregistrynewMqttRouteRegistry();// 扫描当前程序集包含所有 ViewModel 和 Serviceregistry.ScanAssembly(Assembly.GetExecutingAssembly());services.AddSingleton(registry);services.AddSingletonMqttMessageDispatcher();//所有包含 [MqttTopic] 特性的类必须注册到容器中services.AddSingletonMainWindowViewModel();调用[MqttTopic(PLC01/DB7000.58.1)] private async Task UpdateMode(MqttMessageContext context) { Console.WriteLine($收到消息{context.payload}); }
WPF中使用路由模式处理Mqtt消息
直接上代码特征[AttributeUsage(AttributeTargets.Method,AllowMultipletrue,Inheritedfalse)]publicclassMqttTopicAttribute:Attribute{publicstringTopic{get;}/// summary/// 描述可选用于日志/文档/// /summarypublicstringDescription{get;set;};publicMqttTopicAttribute(stringtopic){if(string.IsNullOrWhiteSpace(topic))thrownewArgumentException(Topic 不能为空,nameof(topic));Topictopic;}}注册路由/// summary/// 路由注册/// /summarypublicclassMqttRouteRegistry{publicDictionarystring,List(Type ServiceType,MethodInfo Method)Routes{get;}new(StringComparer.OrdinalIgnoreCase);publicvoidScanAssembly(Assemblyassembly){foreach(vartypeinassembly.GetTypes()){varmethodstype.GetMethods(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);foreach(varmethodinmethods){//使用 GetCustomAttributes 获取方法上贴的所有 MqttTopic 特性varattributesmethod.GetCustomAttributesMqttTopicAttribute();foreach(varattributeinattributes){// 验证方法签名必须包含且仅包含一个 stringvarparametersmethod.GetParameters();// 校验参数类型是否为 MqttMessageContextif(parameters.Length!1||parameters[0].ParameterType!typeof(MqttMessageContext)){thrownewInvalidOperationException($[MqttTopic] 方法{type.Name}.{method.Name}必须有且仅有一个 MqttMessageContext 参数);}// 验证返回值if(method.ReturnType!typeof(Task)method.ReturnType!typeof(void)){thrownewInvalidOperationException($[MqttTopic] 方法{type.Name}.{method.Name}返回值必须是 void 或 Task);}// 初始化或追加到列表if(!Routes.TryGetValue(attribute.Topic,outvarhandlerList)){handlerListnewList(Type,MethodInfo)();Routes[attribute.Topic]handlerList;}handlerList.Add((type,method));}}}}}路由分发publicrecordMqttMessageContext(stringtopic,stringpayload);/// summary/// 路由分发/// /summarypublicclassMqttMessageDispatcher{privatereadonlyMqttRouteRegistry_registry;privatereadonlyIServiceProvider_serviceProvider;publicMqttMessageDispatcher(MqttRouteRegistryregistry,IServiceProviderserviceProvider){_registryregistry;_serviceProviderserviceProvider;}publicasyncTaskDispatchMessageAsync(stringtopic,stringpayload){// 1. 如果没有任何类订阅这个 Topic直接退出if(!_registry.Routes.TryGetValue(topic,outvarhandlers)||handlers.Count0){return;}// 2. 创建一个顶层的作用域或者为每个订阅者创建独立作用域。// 这里推荐采用“一个 Topic 创建一个 Scope”如果追求极致隔离也可以在 foreach 内部单独 Scope。using(varscope_serviceProvider.CreateScope()){vartasksnewListTask();// 3. 遍历所有订阅了该 Topic 的处理器实现一对多广播foreach(varhandlerinhandlers){// 使用 Task.Run 进行并行多线程分发防止订阅者 A 耗时导致订阅者 B 延迟收到消息tasks.Add(Task.Run(async(){try{// 动态向 DI 容器请求实例例如 MainWindowViewModel、AlarmService 等varinstancescope.ServiceProvider.GetService(handler.ServiceType);if(instancenull)return;varcontextnewMqttMessageContext(topic,payload);// 执行方法if(handler.Method.ReturnTypetypeof(Task)){await(Task)handler.Method.Invoke(instance,newobject[]{context});}else{handler.Method.Invoke(instance,newobject[]{context});}}catch(Exceptionex){// 单个订阅者执行崩溃捕获它不影响其他类接收消息System.Diagnostics.Debug.WriteLine($[Dispatcher] 订阅者{handler.ServiceType.Name}处理失败:{ex.Message});}}));}// 等待当前 Topic 的所有订阅者全部处理完毕awaitTask.WhenAll(tasks);}}}依赖注入// 1. 初始化并注册 MQTT 路由注册表,所有消息处理的类必须注入varregistrynewMqttRouteRegistry();// 扫描当前程序集包含所有 ViewModel 和 Serviceregistry.ScanAssembly(Assembly.GetExecutingAssembly());services.AddSingleton(registry);services.AddSingletonMqttMessageDispatcher();//所有包含 [MqttTopic] 特性的类必须注册到容器中services.AddSingletonMainWindowViewModel();调用[MqttTopic(PLC01/DB7000.58.1)] private async Task UpdateMode(MqttMessageContext context) { Console.WriteLine($收到消息{context.payload}); }