Event Mechanism of nopCommerce 3.9 Big Wave Series (Producers, Consumers)
1. Introduction of nop event mechanism
Scenario: After successful payment, customers need to send SMS and email to inform them that the order payment is successful (SMS and email are implemented by different modules)
Implementation: 1. Define OrderPaidEvent event for successful payment.
2. Define SMS, mailbox two consumers jointly monitor the OrderPaidEvent event and implement related business.
3. The producer sends an OrderPaidEvent event when the customer pays successfully.
4. After the consumer receives the OrderPaidEvent event, the SMS and Mailbox consumers perform their respective business.
The nop event mechanism uses the producer/consumer model.Producers are only responsible for publishing events, and do not care who handles them. Instead, consumers are only responsible for handling events.How do producers and consumers relate?The nop implementation is very simple, defining an event class through generics, and if both producers and consumers use the same event class, then the association is called a subscription.The part responsible for implementing the event mechanism itself is called a buffer, which is used to decouple the message mechanism.The producer-consumer relationship is one-to-many.The following figure briefly describes the producer-consumer relationship.
2. nop Event Related Interfaces
Producer interface: Nop.Services.Events.IEventPublisher
Consumer interface: Nop.Services.Events.IConsumer<T>
Event Subscription Interface: Nop.Services.Events.ISubscriptionService
The IEventPublisher interface Publish <T> (T eventMessage) method is used to publish events (producers).
The IConsumer <T>interface HandleEvent(T eventMessage) method is used to handle events (consumers).
The relationship between the two is related by a T generic, called an event, or simply the same T type, where the two associate subscriptions successfully.
The ISubscriptionService interface GetSubscriptions<T>() method returns the IList<IConsumer<T> collection, which saves the consumer.
The interface implementation is as follows:
1 using System;
2 using System.Linq;
3 using Nop.Core.Infrastructure;
4 using Nop.Core.Plugins;
5 using Nop.Services.Logging;
6
7 namespace Nop.Services.Events
8 {
9 /// <summary>
10 /// Evnt publisher
11 /// </summary>
12 public class EventPublisher : IEventPublisher
13 {
14 private readonly ISubscriptionService _subscriptionService;
15
16 /// <summary>
17 /// Ctor
18 /// </summary>
19 /// <param name="subscriptionService"></param>
20 public EventPublisher(ISubscriptionService subscriptionService)
21 {
22 _subscriptionService = subscriptionService;
23 }
24
25 /// <summary>
26 /// Publish to cunsumer
27 /// </summary>
28 /// <typeparam name="T">Type</typeparam>
29 /// <param name="x">Event consumer</param>
30 /// <param name="eventMessage">Event message</param>
31 protected virtual void PublishToConsumer<T>(IConsumer<T> x, T eventMessage)
32 {
33 //Ignore not installed plugins
34 var plugin = FindPlugin(x.GetType());
35 if (plugin != null && !plugin.Installed)
36 return;
37
38 try
39 {
40 //Consumer Processing
41 x.HandleEvent(eventMessage);
42 }
43 catch (Exception exc)
44 {
45 //log error
46 var logger = EngineContext.Current.Resolve<ILogger>();
47 //we put in to nested try-catch to prevent possible cyclic (if some error occurs)
48 try
49 {
50 logger.Error(exc.Message, exc);
51 }
52 catch (Exception)
53 {
54 //do nothing
55 }
56 }
57 }
58
59 /// <summary>
60 /// Find a plugin descriptor by some type which is located into its assembly
61 /// </summary>
62 /// <param name="providerType">Provider type</param>
63 /// <returns>Plugin descriptor</returns>
64 protected virtual PluginDescriptor FindPlugin(Type providerType)
65 {
66 if (providerType == null)
67 throw new ArgumentNullException("providerType");
68
69 if (PluginManager.ReferencedPlugins == null)
70 return null;
71
72 foreach (var plugin in PluginManager.ReferencedPlugins)
73 {
74 if (plugin.ReferencedAssembly == null)
75 continue;
76
77 if (plugin.ReferencedAssembly.FullName == providerType.Assembly.FullName)
78 return plugin;
79 }
80
81 return null;
82 }
83
84 /// <summary>
85 ///Send Events
86 /// </summary>
87 /// <typeparam name="T">Type</typeparam>
88 /// <param name="eventMessage">Event message</param>
89 public virtual void Publish<T>(T eventMessage)
90 {
91 var subscriptions = _subscriptionService.GetSubscriptions<T>();//Get Subscriber
92 subscriptions.ToList().ForEach(x => PublishToConsumer(x, eventMessage));
93 }
94
95 }
96 }
97
EventPublisher
1 using System.Collections.Generic;
2 using Nop.Core.Infrastructure;
3
4 namespace Nop.Services.Events
5 {
6 /// <summary>
7 ///Event Subscription Service
8 /// </summary>
9 public class SubscriptionService : ISubscriptionService
10 {
11 /// <summary>
12 ///Get event subscriptions
13 /// </summary>
14 /// <typeparam name="T">Type</typeparam>
15 /// <returns>Event consumers</returns>
16 public IList<IConsumer<T>> GetSubscriptions<T>()
17 {
18 return EngineContext.Current.ResolveAll<IConsumer<T>>();
19 }
20 }
21 }
22
2. Consumer IConsermer <T>Registration
Register all classes that implement the IConsumer <T>interface in the Nop.Web.Framework.DependencyRegistrar at application startup into the ioc container.
A subscription to a message (T) is available through EngineContext.Current.ResolveAll<IConsumer<T>().
1 //Register Event Consumer
2 var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
3 foreach (var consumer in consumers)
4 {
5 builder.RegisterType(consumer)
6 .As(consumer.FindInterfaces((type, criteria) =>
7 {
8 var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
9 return isMatch;
10 }, typeof(IConsumer<>)))
11 .InstancePerLifetimeScope();
12 }
3. Creating consumers
In conjunction with the scenario mentioned above, we create a consumer subscribing to OrderPaidEvent events to handle SMS notifications.
Create OrderPaidSMSEventConsumer class
1 using System;
2 using Nop.Core;
3 using Nop.Core.Domain.Orders;
4 using Nop.Core.Plugins;
5 using Nop.Services.Events;
6 using Nop.Services.Orders;
7
8 namespace Nop.Plugin.SMS
9 {
10 public class OrderPaidSMSEventConsumer : IConsumer<OrderPaidEvent>
11 {
12
13 private readonly IOrderService _orderService;
14
15 public OrderPaidSMSEventConsumer(
16 IOrderService orderService,
17 IStoreContext storeContext)
18 {
19 this._orderService = orderService;
20 this._storeContext = storeContext;
21 }
22
23 /// <summary>
24 ///Event handling.
25 /// </summary>
26 /// <param name="eventMessage">The event message.</param>
27 public void HandleEvent(OrderPaidEvent eventMessage)
28 {
29
30 var order = eventMessage.Order;//Get Order
31
32 //Send SMS Notification Code
33 //....................
34 }
35 }
36 }
The OrderPaidSMSEventConsumer class inherits IConsumer<OrderPaidEvent>, which is the event class that maintains the subscription relationship between producers and consumers.The event class name can be customized to represent an event.
Next, we create a consumer OrderPaidEmailEventConsumer class for mail processing, which also inherits ICnsumer<OrderPaidEvent>, indicating that we are subscribing to OrderPaidEvent events as well.
1 using System;
2 using Nop.Core;
3 using Nop.Core.Domain.Orders;
4 using Nop.Core.Plugins;
5 using Nop.Services.Events;
6 using Nop.Services.Orders;
7
8 namespace Nop.Plugin.Email
9 {
10 public class OrderPaidEmailEventConsumer : IConsumer<OrderPaidEvent>
11 {
12
13 private readonly IOrderService _orderService;
14 private readonly IStoreContext _storeContext;
15
16 public OrderPaidEmailEventConsumer(
17 IOrderService orderService,
18 IStoreContext storeContext)
19 {
20
21 this._orderService = orderService;
22 this._storeContext = storeContext;
23 }
24
25 /// <summary>
26 ///Mail handling
27 /// </summary>
28 /// <param name="eventMessage">The event message.</param>
29 public void HandleEvent(OrderPaidEvent eventMessage)
30 {
31
32
33 var order = eventMessage.Order;
34
35 //Send Mail to Notify Customer
36 //............................
37 }
38 }
39 }
4. Production messages
We have created two consumers who have subscribed to the OrderPaidEvent event. Now let's see how we notify consumers when their payments are complete.
In the Nop.Services.OrderProcessingService class
The _eventPublisher.Publish(new OrderPaidEvent(order)) method sends an OrderPaidEvent event.The consumption (SMS, Mail) of the above subscription to the OrderPaidEvent event handles the message.
5. Event collation commonly used in nop
Consumers, mainly caching
Nop.Web.Infrastructure.Cache.ModelCacheEventConsumer: Foreground model correlation
Nop.Admin.Infrastructure.Cache.ModelCacheEventConsumer: Background model correlation
Nop.Services.Discounts.Cache.DiscountEventConsumer: Discount related
Nop.Services.Catalog.Cache.PriceCacheEventConsumer: Price Related
Nop.Services.Customers.Cache.CustomerCacheEventConsumer: Password modification
Producers, the following figure summarizes the incidents that come with the nop 3.9 source code and the classes they belong to, mostly the unrealized consumers.
nop just leaves a place for events where we can expand on second development.
6. Summary
1. Producers need to inherit the IEventPublisher interface.
2. Consumers need to inherit the IConsumer<T>interface.
3. Consumers subscribe to producers through event classes, and subscription implementations refer to the ISubscriptionService interface.
The nop event mechanism is simple to implement, and interested friends can use RabbitMQ to expand their messages.
There are incorrect understandings and incorrect views in the text. Please leave a message and share your common progress.
This article address: http://www.cnblogs.com/yaoshangjin/p/7234522.html
This is an original and reproduced version of Big Wave. Please indicate the source.