概述

看源码是一件吃力又很爽的事情,昨天还被搞的一头雾水,今天忽然守得云开见月明。明白它设计意图的同时,感觉自己又提升了一步:)

Orchard刚开始看往往毫无头绪,建议可以从Orchard.Framework&UnitTest入手,先弄明白底层框架机制,抽丝剥茧,一步一步农村包围城市。不用着急,说不定哪天睡觉一下子就理解了。

今天看一下它的事件通知模块的设计,相关类

1.IEventBus  事件总线接口

 public interface IEventBus : IDependency {
IEnumerable Notify(string messageName, IDictionary<string, object> eventData);
}

只提供了一个Notify方法,用于事件通知。

2.DefaultOrchardEventBus 事件总线具体实现

相当于实现一个中转,根据messageName反射调用实现IEventHandler的方法

3.DelegateHelper 委托辅助类

静态类,委托方式调用方法

4.EventsInterceptor 事件拦截器

使用Castle.DynamicProxy作为AOP的实现

5.EventsModule 注册事件拦截器

系统启动时注册Events模块

6.EventsRegistrationSource 实现Orchard动态注入总线接口

Autofac动态依赖注入实现

7.IEventHandler 事件处理类

具体处理业务

实现

看一下它们是如何整合到一起工作的。

1.注册相关模块

 private IContainer _container;
private IEventBus _eventBus;
private StubEventHandler _eventHandler; //[SetUp]
public void Init() {
_eventHandler = new StubEventHandler(); var builder = new ContainerBuilder();
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>();
builder.RegisterType<StubExceptionPolicy>().As<IExceptionPolicy>(); builder.RegisterType<StubEventHandler2>()
.Named(typeof(ITestEventHandler).Name, typeof(IEventHandler))
.Named(typeof(IEventHandler).Name, typeof(IEventHandler))
.WithMetadata("Interfaces", typeof(StubEventHandler2).GetInterfaces().ToDictionary(i => i.Name));
builder.RegisterInstance(_eventHandler)
.Named(typeof(ITestEventHandler).Name, typeof(IEventHandler))
.Named(typeof(IEventHandler).Name, typeof(IEventHandler))
.WithMetadata("Interfaces", typeof(StubEventHandler).GetInterfaces().ToDictionary(i => i.Name)); _container = builder.Build();
_eventBus = _container.Resolve<IEventBus>();
}

事件处理类

 public interface ITestEventHandler : IEventHandler {
void Increment();
void Sum(int a);
void Sum(int a, int b);
void Sum(int a, int b, int c);
void Substract(int a, int b);
void Concat(string a, string b, string c);
IEnumerable<string> Gather(int a, string b);
string GetString();
int GetInt();
} public class StubEventHandler : ITestEventHandler {
public int Count { get; set; }
public int Result { get; set; }
public string Summary { get; set; } public void Increment() {
Count++;
} public void Sum(int a) {
Result = 3 * a;
} public void Sum(int a, int b) {
Result = 2 * (a + b);
} public void Sum(int a, int b, int c) {
Result = a + b + c;
} public void Substract(int a, int b) {
Result = a - b;
} public void Concat(string a, string b, string c) {
Summary = a + b + c;
} public IEnumerable<string> Gather(int a, string b) {
yield return String.Format("[{0},{1}]", a, b);
} public string GetString() {
return "Foo";
} public int GetInt() {
return 1;
}
}
public class StubEventHandler2 : ITestEventHandler {
public void Increment() {
} public void Sum(int a) {
} public void Sum(int a, int b) {
} public void Sum(int a, int b, int c) {
} public void Substract(int a, int b) {
} public void Concat(string a, string b, string c) {
} public IEnumerable<string> Gather(int a, string b) {
return new[] { a.ToString(), b };
} public string GetString() {
return "Bar";
} public int GetInt() {
return 2;
}
}

2.使用(UnitTest)

Assert.That(_eventHandler.Count, Is.EqualTo(0));
_eventBus.Notify("ITestEventHandler.Increment", new Dictionary<string, object>());
Assert.That(_eventHandler.Count, Is.EqualTo(1));

Notify方法的字符串"ITestEventHandler.Increment" 就是  interface + method,DefaultOrchardEventBus接受到这个字符串分解委托调用StubEventHandler的Increment方法。

动态注入&拦截器

完成这个功能主要依靠 EventsRegistrationSource.cs和EventsInterceptor.cs,前者负责动态注入事件总线,后者负责拦截处理。看它的源码前最好了解下Autofac和Castle.DynamicProxy.

最简单的示例演示

1.注册模块

            var builder = new ContainerBuilder();
builder.RegisterType<StubEventBus>().As<IEventBus>();
builder.RegisterSource(new EventsRegistrationSource());
builder.RegisterType<TestClass>();
_container = builder.Build();

2.相关类

 [Test]
public void MyTest()
{
var builder = new ContainerBuilder();
builder.RegisterType&lt;StubEventBus&gt;().As&lt;IEventBus&gt;();
builder.RegisterSource(new EventsRegistrationSource());
builder.RegisterType&lt;TestClass&gt;();
_container = builder.Build(); var c =_container.Resolve&lt;TestClass&gt;();
c.Invoke();
Assert.Fail();
} public class TestClass {
private readonly ICustomerEventHandler eventHandler;
public TestClass(ICustomerEventHandler eventHandler) {
this.eventHandler = eventHandler;
} public void Invoke()
{
eventHandler.Changed("AAA");
}
} public class StubEventBus : IEventBus
{
public string LastMessageName { get; set; }
public IDictionary<string, object> LastEventData { get; set; } public IEnumerable Notify(string messageName, IDictionary<string, object> eventData)
{
Assert.IsTrue(eventData["str1"] == "AAA");
Assert.Pass();
return new object[0];
}
}
public interface ICustomerEventHandler : IEventHandler
{
void Changed(string str1);
}

3.执行代码

 var c =_container.Resolve<TestClass>();
c.Invoke();
Assert.Fail()

原理:

ICustomerEventHandler 没有实现类,并且也没有注册,能运行成功就是靠着EventsRegistrationSource和EventsInterceptor为它进行了动态注册和生成了代理类。

EventsInterceptor负责拦截所有实现IEventHandler类的方法,当调用Changed方法时,拦截并且调用所有IEventBus.Notify方法,eventData存储调用参数值。

Orchard源码:EventBus&EventHandler的更多相关文章

  1. Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)

    概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...

  2. Orchard 源码探索(Log)

    简单工厂模式.抽象工厂模式和适配器模式 依赖倒置原则也叫依赖倒转原则,Dependence Inversion Principle,对抽象进行编程,不要对实现进行编程. A.高层次的模块不应该依赖于低 ...

  3. Orchard源码分析(7.1):Routing(路由)相关

    概述 关于ASP.NET MVC中路由有两个基本核心作用,一是通过Http请求中的Url参数等信息获取路由数据(RouteData),路由数据包含了area.controller.action的名称等 ...

  4. Orchard源码分析(4.4):Orchard.Caching.CacheModule类

    概述 CacheModule也是一个Autofac模块.   一.CacheModule类 CacheModule将DefaultCacheManager注册为ICacheManager:       ...

  5. Orchard源码分析(6):Shell相关

    概述在Orchard中,提出子站点(Tenant)的概念,目的是为了增加站点密度,即一个应用程序域可以有多个子站点. Shell是子站点(Tenant)级的单例,换句话说Shell代表了子站点.对比来 ...

  6. Orchard源码分析(5.1):Host初始化(DefaultOrchardHost.Initialize方法)

    概述 Orchard作为一个可扩展的CMS系统,是由一系列的模块(Modules)或主题(Themes)组成,这些模块或主题统称为扩展(Extensions).在初始化或运行时需要对扩展进行安装:De ...

  7. Orchard源码分析(4.3):Orchard.Events.EventsModule类(Event Bus)

    概述 采用Event Bus模式(事件总线),可以使观察者模式中的观察者和被观察者实现解耦. 在.Net 中使用观察者模式,可以使用事件(委托)和接口(类).Orchard Event  Bus使用的 ...

  8. Orchard源码分析(4.1):Orchard.Environment.CollectionOrderModule类

    CollectionOrderModule类是一个Autofac模块(Module,将一系列组件和相关的功能包装在一起),而非Orchard模块.其作用是保证多个注册到容器的组件能按FIFO(Firs ...

  9. Orchard源码分析(3):Orchard.WarmupStarter程序集

    概述 Orchard.WarmupStarter程序集包含三个类:WarmupUtility.WarmupHttpModule和Starter<T>.该程序集主要为Orchard应用启动初 ...

随机推荐

  1. HTTP调用接口方法

    1.创建接口调用方法类 package cn.com.victorysoft.sjzx.Message; import java.io.BufferedReader; import java.io.I ...

  2. 前端必学内容:webpack3快速入门 1-23节内容参考

    前端必学内容:webpack(模块打包器) webpack3 学习内容,点击即可到达 (1).webpack快速入门——如何安装webpack及注意事项 (2).webpack快速入门——webpac ...

  3. mybatis pagehelper多数据源配置的坑

    我用spring boot配置了2个数据源的工程用来同步不同库的数据,发现如果配置成如下格式报错 #分页配置pagehelper: helper-dialect: mysql reasonable: ...

  4. 爬虫实战1:使用requests和正则爬取电影信息

    代码如下 # coding=utf-8 import requests from requests.exceptions import RequestException import re impor ...

  5. 架构师养成记--37.简单shell编程

    一.HelloWord.sh echo 表示打印,可以在sh文件中写诸如pwd.ls 这样的命令,使用命令的时候尽量使用全路径. #!/bin/sh #this is my first sh echo ...

  6. Spark累加器

    spark累计器 因为task的执行是在多个Executor中执行,所以会出现计算总量的时候,每个Executor只会计算部分数据,不能全局计算. 累计器是可以实现在全局中进行累加计数. 注意: 累加 ...

  7. 达人篇:6.3)试验设计DOE,Design of Experiments

    本章目的:了解DOE,结构工程师为什么学习DOE. 1.前言:结构工程师为什么要学DOE 作者作为一名结构工程师,为什么要学习DOE. 很简单,在第四版FMEA手册中,DOE是重要的探测控制手段.如图 ...

  8. webpack/gulp的z-index被改写

    webpack方法 new OptimizeCSSPlugin({ cssProcessorOptions: { safe: true } }) gulp-cssnano 方法 .pipe(cssna ...

  9. WPF实现WORD 2013墨迹批注功能

    1 前言 WORD 2013可以使用墨迹在文档上面标注,本文讲述通过WPF第三方控件实现类似主要功能如下: 名称 描述 墨迹标注 不论是否触摸屏环境下可以开始墨迹功能,并实现鼠标/触摸在文档任意位置绘 ...

  10. service iptables xxx无效命令的情况下,如何启动/重启iptables

    最近在CentOS 7.6下使用service iptables xxx相关命令,提示如下错误:The service command supports only basic LSB actions ...