Dans le framework Nop, vous pouvez voir que le mécanisme d'événement est utilisé à de nombreux endroits, en particulier la mise à jour du cache. Certaines personnes peuvent se demander, quel problème cela résout-il?
Si nous avons un tel scénario, après l'enregistrement d'un client, nous mettrons à jour le cache, puis enverrons un e-mail d'enregistrement, la pratique habituelle est:
void InsertCustomer()
{
1. 新增到数据库
2. 插入到缓存
3. 发送注册邮件
}
Cela ne pose aucun problème, mais ces codes seront étroitement associés. Si vous souhaitez ajouter une autre action pour envoyer des coupons après l'enregistrement, vous pouvez également ajouter une action pour envoyer des coupons à l'intérieur, afin que l'entreprise soit sécurisée. La reliure est fermement ancrée dans la nouvelle méthode, comment fait Nop?
void Insert()
{
1. 新增到数据库
2. 发布一条新增消息
}
class CacheConsumer: IConsumer<EntityInsertedEvent<Customer>>
{
public void HandleEvent(EntityUpdatedEvent<Setting> eventMessage)
{
//新增缓存
}
}
class MailConsumer: IConsumer<EntityInsertedEvent<Customer>>
{
public void HandleEvent(EntityUpdatedEvent<Setting> eventMessage)
{
//发送邮件
}
}
Expliquez spécifiquement le processus d'exécution:
- Après avoir ajouté un élément de données, publiez un message dans le gestionnaire de messages
- Tous les consommateurs qui s'abonnent au nouveau message recevront le message
- Les consommateurs exécutent des événements spécifiques, tels que l'ajout de cache, l'envoi d'e-mails, etc.
L'implémentation technique de Nop peut être un peu plus compliquée. Au lieu d'utiliser RabbitMQ traditionnel et d'autres files d'attente de messages tiers, il combine l'injection de dépendance d'Autofac pour réaliser intelligemment cette conception:
- Interface de publication d'événements IEventPublisher
- EventPublisher implémente la classe IEventPublisher, les fonctions principales incluent la publication d'événements et la notification des abonnés
- IConsumer
Interface client (abonné à l'événement) - Interface d'abonné ISubscriptionService, résoudre tous les abonnés
- Implémentation spécifique de SubscriptionService
Ensuite, jetons un coup d'œil à la classe d'implémentation spécifique, en prenant CategoryService comme exemple, pour supprimer les détails inutiles:
public virtual void InsertCategory(Category category)
{
_categoryRepository.Insert(category);
//发布一条消息
_eventPublisher.EntityInserted(category);
}
La classe de PriceCacheEventConsumer qui s'abonne à ce message a le code suivant:
public partial class PriceCacheEventConsumer : IConsumer<EntityInsertedEvent<Category>>
{
public void HandleEvent(EntityInsertedEvent<Category> eventMessage)
{
_cacheManager.RemoveByPattern(NopCatalogDefaults.ProductCategoryIdsPatternCacheKey);
}
}
Le code ci-dessus a supprimé le facteur d'interférence, nous voyons que cette classe hérite de IConsumer
Ensuite, regardons ce que fait EventPublisher:
/// <summary>
/// 发布到消费者
/// </summary>
protected virtual void PublishToConsumer<T>(IConsumer<T> x, T eventMessage)
{
try
{
x.HandleEvent(eventMessage); //执行消费者订阅方法
}
catch (Exception exc)
{
}
}
/// <summary>
/// 发布事件,注意,是整个实现的核心
/// </summary>
public virtual void Publish<T>(T eventMessage)
{
//利用控制反转IOC技术,查找所有消费者类
var subscribers = _subscriptionService.GetSubscriptions<T>()
.Where(subscriber => PluginManager.FindPlugin(subscriber.GetType())?.Installed ?? true).ToList();
//调用PublishToConsumer,执行对应消费者类的方法,这个是关键
subscribers.ForEach(subscriber => PublishToConsumer(subscriber, eventMessage));
}
Service de publication d'événements: SubscriptionService, cette classe est une classe d'outils, fournit une méthode pour trouver des consommateurs en fonction du type GetSubscriptions
public IList<IConsumer<T>> GetSubscriptions<T>()
{
return EngineContext.Current.ResolveAll<IConsumer<T>>().ToList();
}
EngineContext dans la classe ci-dessus, gestionnaire de moteur Nop, Current fait référence au moteur actuel, ResolveAll prend la classe consommateur du conteneur, voir Autofac pour la technologie spécifique
Pour résumer:
- Le code entier réalise le découplage et ne dépend plus les uns des autres
- Publier un abonnement aux messages, n'utilise pas directement la file d'attente de messages tiers RabbitMQ, il n'y a donc pas de terminal de surveillance séparé
- En utilisant la technologie d'inversion de contrôle, résout intelligemment la difficulté de trouver des consommateurs, si vous utilisez la réflexion, les performances diminueront