从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案 (2)

从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案

如何实现插件间的消息传递?

这个问题是去年年底和衣明志大哥讨论动态插件开发的时候,衣哥提出来的功能,本身实现思路不麻烦,但是实践过程中,我却让AssemblyLoadContext给绊了一跤。

基本思路

这里因为需要实现两个不同插件的消息通信,最简单的方式是使用消息注册订阅。

PS: 使用第三方消息队列也是一种实现方式,但是本次实践中只是为了简单,没有使用额外的消息注册订阅组件,直接使用了进程内的消息注册订阅

从零开始实现ASP.NET Core MVC的插件式开发(七) - 近期问题汇总及部分解决方案

基本思路:

定义INotificationHandler接口来处理消息

在每个独立组件中,我们通过INotificationProvider接口向主程序公开当前组件订阅的消息及处理程序

在主站点中,我们通过INotificationRegister接口实现一个消息注册订阅容器,当站点启动,系统可以通过每个组件的INotificationProvider接口实现,将订阅的消息和处理程序注册到主站点的消息发布订阅容器中。

每个插件中,使用INotifcationRegister接口的Publish方法发布消息

根据以上思路,我们首先定义一个消息处理接口INotification

public interface INotificationHandler { void Handle(string data); }

这里我没有采用强类型的来规范消息的格式,主要原因是如果使用强类型定义消息,不同的插件势必都要引用一个存放强类型强类型消息定义的的程序集,这样会增加插件之间的耦合度,每个插件就开发起来变得不那么独立了。

PS: 以上设计只是个人喜好,如果你喜欢使用强类型也完全没有问题。

接下来,我们再来定义消息发布订阅接口以及消息处理程序接口

public interface INotificationProvider { Dictionary<string, List<INotificationHandler>> GetNotifications(); } public interface INotificationRegister { void Subscribe(string eventName, INotificationHandler handler); void Publish(string eventName, string data); }

这里代码非常的简单,INotificationProvider接口提供一个消息处理器的集合,INotificationRegister接口定义了消息订阅和发布的方法。

下面我们在Mystique.Core.Mvc项目中完成INotificationRegister的接口实现。

public class NotificationRegister : INotificationRegister { private static Dictionary<string, List<INotificationHandler>> _containers = new Dictionary<string, List<INotificationHandler>>(); public void Publish(string eventName, string data) { if (_containers.ContainsKey(eventName)) { foreach (var item in _containers[eventName]) { item.Handle(data); } } } public void Subscribe(string eventName, INotificationHandler handler) { if (_containers.ContainsKey(eventName)) { _containers[eventName].Add(handler); } else { _containers[eventName] = new List<INotificationHandler>() { handler }; } } }

最后,我们还需要在项目启动方法MystiqueSetup中配置消息订阅器的发现和绑定。

public static void MystiqueSetup(this IServiceCollection services, IConfiguration configuration) { ... using (IServiceScope scope = provider.CreateScope()) { ... foreach (ViewModels.PluginListItemViewModel plugin in allEnabledPlugins) { ... using (FileStream fs = new FileStream(filePath, FileMode.Open)) { ... var providers = assembly.GetExportedTypes() .Where(p => p.GetInterfaces() .Any(x => x.Name == "INotificationProvider")); if (providers != null && providers.Count() > 0) { var register = scope.ServiceProvider .GetService<INotificationRegister>(); foreach (var p in providers) { var obj = (INotificationProvider)assembly .CreateInstance(p.FullName); var result = obj.GetNotifications(); foreach (var item in result) { foreach (var i in item.Value) { register.Subscribe(item.Key, i); } } } } } } } ... }

完成以上基础设置之后,我们就可以尝试在插件中发布订阅消息了。

首先这里我们在DemoPlugin2中创建消息LoadHelloWorldEvent,并创建对应的消息处理器LoadHelloWorldEventHandler.

public class NotificationProvider : INotificationProvider { public Dictionary<string, List<INotificationHandler>> GetNotifications() { var handlers = new List<INotificationHandler> { new LoadHelloWorldEventHandler() }; var result = new Dictionary<string, List<INotificationHandler>>(); result.Add("LoadHelloWorldEvent", handlers); return result; } } public class LoadHelloWorldEventHandler : INotificationHandler { public void Handle(string data) { Console.WriteLine("Plugin2 handled hello world events." + data); } } public class LoadHelloWorldEvent { public string Str { get; set; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjszd.html