Nop有着完善的事件机制,在框架中也多次用到。比如删除删除电子邮件时删除指定的缓存、更新实体时,更新缓存等。这里用到的是“生产者/消费者”模式,该模式中,定义了对象之间一对多的依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。生产者发布事件,消费者处理事件。
一.相关接口和类:
1.IEventPublisher事件发布接口。
2.EventPublisher实现IEventPublisher的类,主要功能包括发布事件并通知订阅者。
3.IConsumer<T>消费者(事件订阅者)接口。
4.ISubscriptionService订阅者接口,解析所有的订阅者。
5.SubscriptionService的具体实现。
二注册事件:
如果用户想要侦听事件,就需要注册成为消费者。消费者接口IConsumer<T>为一个泛型接口,里面有一个处理事件的方法。IConsumer<T>接口代码如下:
/// <summary> /// 消费者 /// </summary> public interface IConsumer<T> { void HandleEvent(T eventMessage); }
在该框架中,实现IConsumer<T>接口的类很多比如Nop.Admin.Infrastructure.Cache.ModelCacheEventConsumer代码如下:
public partial class ModelCacheEventConsumer : IConsumer<EntityUpdated<UrlRecord>> { public void HandleEvent(EntityUpdated<UrlRecord> eventMessage) { throw new NotImplementedException(); } #region KEY /// <summary> /// Key for widget info /// </summary> /// <remarks> /// {0} : current store ID /// {1} : widget zone /// {2} : current theme name /// </remarks> public const string WIDGET_MODEL_KEY = "NopFramework.pres.widget-{0}"; public const string WIDGET_PATTERN_KEY = "NopFramework.pres.widget"; #endregion }
“HandleEvent”为对应的事件处理方法,可以根据需要处理。实现了“IConsumer<T>”接口就注册成为了订阅者,当有任何消息发布时,就能真听到对应的事件。
三.事件订阅
事件订阅主要就是解析哪些用户订阅了事件,这样当事件发生时,才能准确的发送到指定的用户。订阅接口如下:
/// <summary> /// 事件订阅服务 /// </summary> public interface ISubscriptionService { IList<IConsumer<T>> GetSubscriptions<T>(); }
该接口只有一个获取一个方法--获取所有注册事件的用户。实现该类具体如下:
/// <summary> /// 事件订阅服务 /// </summary> public class SubscriptionService : ISubscriptionService { public IList<IConsumer<T>> GetSubscriptions<T>() { return EngineContext.Current.ResolveAll<IConsumer<T>>();// } }
四.事件发布
事件发布接口“IEventPublisher”。该接口只包含一个方法:
/// <summary> /// 事件发布接口 /// </summary> public interface IEventPublisher { /// <summary> /// 事件发布 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="eventMessage"></param> void Publish<T>(T eventMessage); }
具体的实现如下:
public class EventPublisher : IEventPublisher { private readonly ISubscriptionService _subscriptionService; /// <summary> /// 构造函数 /// </summary> /// <param name="subscriptionService"></param> public EventPublisher(ISubscriptionService subscriptionService) { this._subscriptionService = subscriptionService; } /// <summary> /// 发布事件,通知订阅者 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="x"></param> /// <param name="eventMessage"></param> public virtual void PublishToConsumer<T>(IConsumer<T> x,T eventMessage) { try { x.HandleEvent(eventMessage); } catch (Exception exc) { var logger= EngineContext.Current.Resolve<ILogger>(); try { logger.Error(exc.Message, exc); } catch (Exception) { } } } /// <summary> /// 根据位于其程序集中的某个类型找到一个插件描述符 /// </summary> /// <param name="providerType"></param> /// <returns></returns> protected virtual PluginDescriptor FindPlugin(Type providerType) { if (providerType == null) throw new ArgumentNullException("providerType"); if (PluginManager.ReferencedPlugins == null) return null; foreach (var plugin in PluginManager.ReferencedPlugins) { if (plugin.ReferencedAssembly == null) continue; if (plugin.ReferencedAssembly.FullName == providerType.Assembly.FullName) return plugin; } return null; } /// <summary> /// 发布事件 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="eventMessage"></param> public virtual void Publish<T>(T eventMessage) { var subscriptions = _subscriptionService.GetSubscriptions<T>();//查找所有注册事件的用户 subscriptions.ToList().ForEach(x=>PublishToConsumer(x,eventMessage)); } }
先看下“Publish<T>(T eventMessage)”方法,该方法是由其他方法比如更新或者删除实体后调用,触发事件。然后调用ISubscriptionService 接口的“GetSubscriptions”方法,查找所有注册该接口的事件,之后逐条遍历订阅者,执行“PublishToConsumer”方法,发布事件,通知订阅者,具体的处理,可在“HandleEvent”实现;