结合DI,实现发布者与订阅者的解耦,属于本次事务的对象主体不应定义为订阅者,因为订阅者不应与发布者产生任何关联

一、发布者订阅者模式

发布者发出一个事件主题,一个或多个订阅者接收这个事件,中间通过事件总线通讯(消息队列),并且发布者与订阅者这两者间是无状态的,根据产品实际场景需要,可以自己实现单机单点的发布订阅,也可选择使用目前流行的分布式消息中间件:

RabbitMQ、ActiveMQ、RocketMQ、kafka等

二、观察者与订阅者的区别

观察者与业务主体是耦合的,并且是即时通知的;订阅者与业务主体完全解耦,只通过中间的信息通道通知,互相不知道对方的存在,可以是同步也可以是异步

三、具体实现

本文主要讲解单点模式,有需要随时可以扩展为分布式方案

发布者接口:

 namespace Xms.Event.Abstractions
{
/// <summary>
/// 事件发布接口
/// </summary>
public interface IEventPublisher
{
/// <summary>
/// 发布事件
/// </summary>
/// <typeparam name="TEvent">事件类型</typeparam>
/// <param name="e"></param>
void Publish<TEvent>(TEvent e);
}
}

发布者实现:

 using System;
using System.Linq;
using Xms.Event.Abstractions;
using Xms.Infrastructure.Inject;
using Xms.Logging.AppLog; namespace Xms.Event
{
/// <summary>
/// 事件发布者
/// </summary>
public class EventPublisher : IEventPublisher
{
private readonly ILogService _logService;
private readonly IServiceResolver _serviceResolver; public EventPublisher(ILogService logService
, IServiceResolver serviceResolver)
{
_logService = logService;
_serviceResolver = serviceResolver;
} #region Methods /// <summary>
/// 发布事件
/// </summary>
/// <typeparam name="TEvent">事件类</typeparam>
/// <param name="e">事件对象</param>
public virtual void Publish<TEvent>(TEvent e)
{
//获取所有事件接收者
var consumers = _serviceResolver.GetAll<IConsumer<TEvent>>().ToList();
foreach (var consumer in consumers)
{
try
{
//处理事件
consumer.HandleEvent(e);
}
catch (Exception exception)
{
_logService.Error(exception);
}
}
} #endregion Methods
}
}

订阅(消费)者接口:

 namespace Xms.Event.Abstractions
{
/// <summary>
/// 事件接收接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IConsumer<T>
{
/// <summary>
/// 处理事件
/// </summary>
/// <param name="eventMessage">事件</param>
void HandleEvent(T eventMessage);
}
}

事件(消息):

这里只给出一个作为示例,实际上一般会有记录的:“创建”、“修改”、“删除”,流程相关的:“发起审批”、“审批通过”、“审批完成”等等

 namespace Xms.Flow.Core.Events
{
/// <summary>
/// 工作流启动后事件
/// </summary>
public class WorkFlowStartedEvent
{
public WorkFlowStartUpContext Context { get; set; }
public WorkFlowExecutionResult Result { get; set; }
}
}

服务注册:

详细实现回看.netcore之DI批量注入(支持泛型)

 using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xms.Core;
using Xms.Infrastructure.Inject; namespace Xms.Event
{
/// <summary>
/// 事件模块服务注册
/// </summary>
public class ServiceRegistrar : IServiceRegistrar
{
public int Order => ; public void Add(IServiceCollection services, IConfiguration configuration)
{
//event publisher
services.AddScoped<Event.Abstractions.IEventPublisher, Event.EventPublisher>();
//event consumers
services.RegisterScope(typeof(Event.Abstractions.IConsumer<>));
}
}
}

四、应用场景

比如在工作流启动审批后发送通知

 using System.Collections.Generic;
using Xms.Context;
using Xms.Event.Abstractions;
using Xms.Flow.Core.Events;
using Xms.Infrastructure.Utility;
using Xms.Localization.Abstractions;
using Xms.Notify.Abstractions;
using Xms.Notify.Internal; namespace Xms.EventConsumers.Notify
{
/// <summary>
/// 工作流启动审批后发送通知
/// </summary>
public class WorkflowStartedNotify : IConsumer<WorkFlowStartedEvent>
{
private readonly IAppContext _appContext;
private readonly ILocalizedTextProvider _loc;
private readonly IEnumerable<INotify> _notifies; public WorkflowStartedNotify(IAppContext appContext
, IEnumerable<INotify> notifies)
{
_appContext = appContext;
_loc = _appContext.GetFeature<ILocalizedTextProvider>();
_notifies = notifies;
}
public void HandleEvent(WorkFlowStartedEvent eventMessage)
{
//当前节点处理人
foreach (var handlerId in eventMessage.Result.NextHandlerId)
{
//通知方式:微信、短信、邮件、系统消息等
var msg = _loc["workflow_newtasknotify"].FormatWith(eventMessage.Context.EntityMetaData.LocalizedName);
//发送消息
foreach (var notifier in _notifies)
{
notifier.Send(new InternalNotifyBody()
{
TypeCode =
,
Subject = msg
,
Content = "到你审批了,快到碗里来"
,
ToUserId = handlerId
,
LinkTo = "/entity/create?entityid=" + eventMessage.Context.EntityMetaData.EntityId + "&recordid=" + eventMessage.Context.ObjectId
});
}
}
}
}
}

四、总结

前面讲解了订阅者模式的基本概念及与观察者的区别,后面展示了具体实现及实际应用场景,大家记住一点就行,这些设计模式最终都是为了达到解藕的目的,要查看完整代码,请回到这一章

xms跨平台基础框架 - 基于.netcore

.netcore利用DI实现订阅者模式 - xms的更多相关文章

  1. .netcore之DI批量注入(支持泛型) - xms

    一旦系统内模块比较多,按DI标准方法去逐个硬敲AddScoped/AddSingleton/AddTransient缺乏灵活性且效率低下,所以批量注入提供了很大的便捷性,特别是对于泛型的服务类,下面介 ...

  2. 利用DI实现级联删除 - xms跨平台基础框架 - 基于.netcore

    一.引言 所谓级联删除是指删除一条记录后,附带关联记录也一起删除,比如删除客户后,联系人也一起删除: 以往我们会依赖于数据库表的外键约束,但存在着明显的问题,增加数据库压力.提示不友好.职责越界.事务 ...

  3. AngularJS的简单订阅发布模式例子

    控制器之间的交互方式广播 broadcast, 发射 emit 事件 类似于 js中的事件 , 可以自己定义事件 向上传递直到 document 在AngularJs中 向上传递直到 rootScop ...

  4. js设计模式之代理模式以及订阅发布模式

    为啥将两种模式放在一起呢?因为这样文章比较长啊. 写博客的目的我觉得首要目的是整理自己的知识点,进而优化个人所得知识体系.知识成为个人的知识,就在于能够用自己的话表达同一种意义. 本文是设计模式系列文 ...

  5. RabbitMQ (五) 订阅者模式之分发模式 ( fanout )

    前面讲到了简单队列和工作队列. 这两种队列有个非常明显的缺点 : 生产者发送的消息,只能进入到一个队列. 消息只能进入到一个队列就意味着消息只能被一个消费者消费. 尽管工作队列模式中,一个队列中的消息 ...

  6. C#事件支持发布者/订阅者模式(观察者模式)

    C#事件支持发布者/订阅者模式,发布者将事件通知给订阅者,而订阅者在事件发生时调用已经注册好的事件处理函数.        public delegate void delUpdate();  //委 ...

  7. Android 订阅-发布者模式-详解

    1.概念简述 Android 简称观察者模式, GoF说道:Observer模式的意图是“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”. 有 ...

  8. Publisher/Subscriber 订阅-发布模式

    Publisher/Subscriber 订阅-发布模式 本博后续将陆续整理这些年做的一些预研demo,及一些前沿技术的研究,与大家共研技术,共同进步. 关于发布订阅有很多种实现方式,下面主要介绍WC ...

  9. 设计模式---订阅发布模式(Subscribe/Publish)

    设计模式---订阅发布模式(Subscribe/Publish) 订阅发布模式定义了一种一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象.这个主题对象在自身状态变化时,会通知所有订阅者对象,使 ...

随机推荐

  1. Android OkHttp + Retrofit 取消请求的方法

    本文链接 前言 在某一个界面,用户发起了一个网络请求,因为某种原因用户在网络请求完成前离开了当前界面,比较好的做法是取消这个网络请求.对于OkHttp来说,具体是调用Call的cancel方法. 如何 ...

  2. 某CTF平台一道PHP代码注入

    这道题以前做过但是没有好好的总结下来.今天又做了一下,于是特地记录于此. 首先就是针对源码进行审计: 关于create_function这个函数可以看一下这个:http://www.php.cn/ph ...

  3. 我在用的翻译软件 -> 微软翻译+网易有道词典+谷歌翻译

    Windows网页翻译 因为微软翻译相对来说翻译网页更为准确,我也喜欢用谷歌的Chrome浏览器,但是我没找到微软翻译的扩展,这里只能放弃 这个需要配合Microsoft Edge浏览器进行使用,也是 ...

  4. 在我的新书里,尝试着用股票案例讲述Python爬虫大数据可视化等知识

    我的新书,<基于股票大数据分析的Python入门实战>,预计将于2019年底在清华出版社出版. 如果大家对大数据分析有兴趣,又想学习Python,这本书是一本不错的选择.从知识体系上来看, ...

  5. Vue学习系列(二)——组件详解

    前言 在上一篇初识Vue核心中,我们已经熟悉了vue的两大核心,理解了Vue的构建方式,通过基本的指令控制DOM,实现提高应用开发效率和可维护性.而这一篇呢,将对Vue视图组件的核心概念进行详细说明. ...

  6. RF中for循环

    robotframework支持FOR循环语句,语法和Python的语法基本相同,但robotframework中,“FOR”关键字前面需要增加一个“:”,写成“:FOR”,其它与Python的语法相 ...

  7. [插件化开发] Poc之后,我选择放弃OSGI

    Poc之后,我选择放弃OSGI TIPS: 如贵司允许重构老系统或者允许使用OSGI的第三方框架改造所带来的投入成本,并且评估之后ROI乐观,那么还是可以使用的. Runtime Version 以下 ...

  8. 数据结构1_C---单链表的逆转

    通过C语言函数实现单链表的逆转操作 例: 输入数据1,2,3,4 输出数据4,3,2,1 一共三个文件: 头文件stulist,h :链表结点的定义,结点指针的定义 源文件stulist.c:具体的实 ...

  9. Prometheus 源码解读(一)

    Prometheus 源码解读(一) Prometheus 是云原生监控领域的事实标准,越来越来的开源项目开始支持 Prometheus 监控数据格式.从本篇开始,我将和大家一起阅读分析 Promet ...

  10. Kubernetes WebSSH终端窗口自适应Resize

    追求完美不服输的我,一直在与各种问题斗争的路上痛并快乐着 上一篇文章Django实现WebSSH操作Kubernetes Pod最后留了个问题没有解决,那就是terminal内容窗口的大小没有办法调整 ...