概述

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

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. 如何让Gogland不过期,一直使用?

    Gogland是jetBrains公司出品的GO语言开发IDE,是目前最好的GO语言开发工具!!但是目前Gogland提供的试用版,有一定的使用期限,如何到期还能使用?经过我的测试,如果Gogland ...

  2. 446. Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  3. DBHelper--Java JDBC SSH 连接数据库工具类

    概述 JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库. ----------------------------- ...

  4. HTML5语义化标签总结

    1.语义化标签总结 基础布局标签 <header></header> <nav></nav> <main></main> < ...

  5. 【vim】插入模式与常用编辑操作

    vim不像很多编辑器那样一启动便可以直接编辑文本,需要在普通模式按下i, a等键才会进入插入模式进行文本编辑. 如何进入插入模式 以下的命令都会让vim从普通模式切换到插入模式,但命令执行后的字符插入 ...

  6. Python的科学计算包matplotlib setup

    回想起大学四年 专业一直使用matlab,然而我却没在PC上装成功过,以前懒于思考这种数学工具的作用,直到最近,大学同学研究生要毕业了,几经交流,和自己阅读了一些机器学习的教材之后,发觉科学计算包和画 ...

  7. [redis]复制机制,调优,故障排查

    在redis的安装目录下首先启动一个redis服务,使用默认的配置文件,作为主服务 ubuntu@slave1:~/redis2$ ./redis-server ./redis.conf & ...

  8. 架构师养成记--25.linux用户管理

    用户管理配置文件用户信息文件:/etc/passwd密码文件:/etc/shadow用户配置文件:/etc/login.defs /etc/default/useradd新用户信息文件:/etc/sk ...

  9. C#-WebForm-LinQ-条件精确查询、高级查询

    前台界面,并在后台绑定数据 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Ca ...

  10. 【转载】SSAS-MDX#001 - MDX 基本结构

    1. MDX 的基本结构 - MDX 的基本结构有三种: Members, Tuple 和 Set    2. Members - 指的是维度树上的一个节点, 这里有一点需要指出, 量度也是一个特殊的 ...