惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
V
V2EX
量子位
Last Week in AI
Last Week in AI
Jina AI
Jina AI
博客园 - 【当耐特】
爱范儿
爱范儿
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
小众软件
小众软件
IT之家
IT之家
博客园_首页
博客园 - 聂微东
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗

博客园 - alby

在 ASP.NET Core Web API 中处理 Patch 请求 一种有界队列(Bounded Buffer)的实现 基于 Mediasoup 的 Abp vNext 视频会议模块 使用 ASP.NET Core 作为 mediasoup 的信令服务器 ASP.NET Core 的 `Core` 有几种写法? ASP.NET Core MVC 授权的扩展:自定义 Authorize Attribute 和 IApplicationModelProvide OrchardCore 如何实现模块化( Modular )和 Multi-Tenancy [iOS]关于状态栏(UIStatusBar)的若干问题 [iOS]关于视频方向的若干问题 --2015-06-24-- Orchard源码分析(7.2):Controller相关 Orchard源码分析(7.1):Routing(路由)相关 Orchard源码分析(7):ASP.NET MVC相关 Orchard源码分析(4.4):Orchard.Caching.CacheModule类 Orchard源码分析(6):Shell相关 Orchard源码分析(5.3):EndRequest事件处理(DefaultOrchardHost.EndRequest方法) Orchard源码分析(5.2):BeginRequest事件处理(DefaultOrchardHost.BeginRequest方法) Orchard源码分析(5.1):Host初始化(DefaultOrchardHost.Initialize方法) Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)
Orchard源码分析(4.3):Orchard.Events.EventsModule类(Eve...
alby · 2012-10-18 · via 博客园 - alby

在.Net 中使用观察者模式,可以使用事件(委托)和接口(类)。Orchard Event Bus使用的是接口的形式,这样方便将“观察者”注册到Autofac容器中。EventsModule模块是构成Orchard Event Bus的一部分。这里先分开分析Orchard Event Bus涉及的类型和知识点,然后在将他们组合起来分析Orchard Event Bus的机制。


一、Registration Source
首先EventsModule也是一个Autofac模块。其注册了一个EventsRegistrationSource对象,所以分析的重点是在EventsRegistrationSource类上。

Registration Source可以用于检索容器要使用的服务(Service,Autofac概念)。

下面用一个简单的例子来说明:
1、创建如下接口和类:

    public interface ICustomerService { }

    public class DefaultCustomerService : ICustomerService { }

2、测试:

        [Test]

        public void TestRegistrationSource()

        {

            ContainerBuilder builder = new ContainerBuilder();

            //builder.RegisterSource(new MyRegistrationSource());         

            IContainer container = builder.Build();

            ICustomerService customerService = container.Resolve<ICustomerService>();

            Assert.That(customerService,Is .Not.Null);

        }

3、ICustomerService没有在容器中进行注册,很显然上面的代码不能测试通过,现在创建一个MyRegistrationSource类,实现IRegistrationSource接口:

     public class MyRegistrationSource : IRegistrationSource

    {

        public IEnumerable <IComponentRegistration> RegistrationsFor( Service service, Func <Service, IEnumerable<IComponentRegistration >> registrationAccessor)

        {

            var serviceWithType = service as IServiceWithType;

            if (serviceWithType == null )

                yield break ;

            var serviceType = serviceWithType.ServiceType;

            if (!serviceType.IsInterface || !typeof (ICustomerService).IsAssignableFrom(serviceType))

                yield break ;

            /*

            //使用RegistrationBuilder.ForType方法

            var rb = RegistrationBuilder

                .ForType<DefaultCustomerService>()

                .As(service);

            */

            //使用RegistrationBuilder.ForDelegate方法

            var rb = RegistrationBuilder

               .ForDelegate((ctx, parameters) =>

               {

                   return new DefaultCustomerService();

               })

               .As(service);

            yield return rb.CreateRegistration();

        }

    }

4、测试:
取消//builder.RegisterSource(new MyRegistrationSource());行的注释,再次测试通过。

上面的例子看起来有点小题大作或者说完全没必要,因为我们已经明确知道接口及其实现类,有两种替代方式可以实现相同的目的。首先可以创建一个 Autofac模块(继承自Module类或IModule接口皆可),在其Load方法中进行注册;或者直接在创建ContainerBuilder对 象的地方注册,比如:

        [Test]

        public void TestRegistrationSource()

        {

            ContainerBuilder builder = new ContainerBuilder();

            builder.RegisterType< DefaultCustomerService>().As<ICustomerService >();

            IContainer container = builder.Build();

            ICustomerService customerService = container.Resolve<ICustomerService>();

            Assert.That(customerService,Is.Not.Null);

        }

但是Registration Source其实是为了提供一种延迟注册机制,比如我们马上要分析的EventsRegistrationSource类。

二、Dynamic Proxy和EventsRegistrationSource
Castls 动态代理可以根据某个类或接口动态创建代理类。当然通过接口实现的代理类的方法中没有任何可用实现。但是可以通过使用注入器(AOP,详见 IInterceptor接口),在代理类的方法执行前、执行后注入一定的逻辑。关于动态代理这里不详述。不过可以想象一下ASP.NET MVC中的Filter。

EventsRegistrationSource类中就使用了接口代 理。对于IEventHandler的子接口的类型(注意不是IEventHandler的直接子类型),则创建一个对应代理类。当方法执行时,交由 DefaultOrchardEventBus来处理。

三、DefaultOrchardEventBus
DefaultOrchardEventBus类的作用是通过反射动态调用一组对象的某一个方法,它提供了缓存机制弥补反射的低性能,并提供了异常处理接口。
该类的构造函数接受两个参数,类型分别是Func<IEnumerable<IEventHandler>>委托和 IExceptionPolicy类型。通过第一个参数可以获取包含调用方法的类序列。通过第二个参数提供异常处理策 略,IExceptionPolicy可以处理异常(比如日志记录),还可以决定是否把异常继续由DefaultOrchardEventBus向外抛 出。
该类还有唯一的公开方法Notify,接受一个字符串和IDictionary<string, object>型参数。字符串由"."号分隔,"."前面是接口短名称(注意不是类名称),后面是被调用方法名称。 IDictionary<string, object>型参数包含调用方法时要传递的参数。通过接口名称和方法名称查找某一个类型的方法并调用。Notify返回IEnumeralbe型 参数,是所有被调用方法返回值的集合。
在某个角度上看,DefaultOrchardEventBus类似于WPF的ObjectDataProvider。

四、Orchard Event Bus机制
有了上面的知识积累,下面通过一个简单的实例来分析Orchard的Event Bus机制。
我们来实现一个简单的观察者模式。
1、新建一个控制台项目,并准备一些接口和类

    public interface IObserver { void Notify(string message);}

    public class Observer1 : IObserver { public void Notify( string message) { Console .WriteLine("Call Observer1.Notify Method:{0}", message); } }

    public class Observer2 : IObserver { public void Notify( string message) { Console .WriteLine("Call Observer2.Notify Method:{0}", message); } }

    public interface ISubject { void DoSomething(); }

    public class Subject : ISubject

    {

        public readonly IList< IObserver> Handlers = new List< IObserver>();

        public void DoSomething()

         {

             Console.WriteLine("Call DoSomething method." );

             foreach(var item in Handlers)

             {

                 item.Notify( "Hello");

             }

         }

    }

    public class Tester

    {

        static void Main(string[] args)

        {

            Subject subject = new Subject();

            subject.Handlers.Add( new Observer1 ());

            subject.Handlers.Add( new Observer2 ());

            subject.DoSomething();

        }

    }

(通过事件或委托也能实现观察者模式,这里不进行讨论)
2、第三方程序集也想得到通知
新建一个类库项目,创建Observer3类

     public class Observer3 : IObserver { public void Notify( string message) { Console .WriteLine("Call Observer3.Notify Method:{0}", message); } }

在不修改控制台的项目源码的前提下,目前来说是没有办法的。我们可以通过反射,在所有程序集中搜索所有观察者,创建其实例并添加到Subject的Handlers中。Orchard Event Bus就用类似的方式很好地解决了这些问题。

修改IObserver接口,使之实现Orchard.Events.IEventHandler接口:

     public interface IObserver : Orchard.Events. IEventHandler { void Notify(string message);}

在Orchard中,所有实现了IEventHandler接口的类型都将被注册到Autofac容器中(更准确地说,是所有实现了Orchard.IDependency接口的类型)。

然后修改在Subject增加一个接收IEnumeralbe<IObserver>或IObserver类型的构造函数:

     public class Subject : ISubject

         private readonly IEnumerable< IObserver> _handlers;

         public Subject(IEnumerable <IObserver> handlers)

         {

             _handlers = handlers;

         }

         public void DoSomething()

         {

             Console.WriteLine("Call DoSomething method." );

             foreach(var item in _handlers)

             {

                 item.Notify( "Hello");

             }

         }

    }

再修改控制台程序:

     public class Tester

    {

        static void Main(string[] args)

        {

            var builder = new ContainerBuilder();

            builder.RegisterModule( new EventsModule ());

            builder.RegisterType< DefaultOrchardEventBus>().As<IEventBus >().SingleInstance();

            builder.RegisterType< DefaultExceptionPolicy>().As<IExceptionPolicy >().SingleInstance();

            builder.RegisterType< Subject>().As<ISubject >();

            builder.RegisterType< Observer1>().As<IEventHandler >();

            builder.RegisterType< Observer2>().As<IEventHandler >();

            //...(通过Autofac自动装配机制,扫描程序集,对所有IEventHandler实现进行注册。此处代码略)

            var container = builder.Build();

            var testEventHandler = container.Resolve<ISubject>();

            testEventHandler.DoSomething();

        }

    }

以上代码略去了自动注册IEventHandler的代码,Orchard是在Orchard.Environment.ShellBuilders.ShellContainerFactory的CreateContainer方法中完成这个工作的。

相关类型:

Orchard.Events.EventsRegistrationSource : Castle.Core.IRegistrationSource
Orchard.Events.IEventHandler : Orchard.IDependency
Orchard.Events.DefaultOrchardEventBus : Orchard.Events.IEventBus
Orchard.Events.EventsInterceptor : Castle.Core.Interceptor.IInterceptor(Dynamic Proxy)

Orchard.Environment.ShellBuilders.ShellContainerFactory

参考资料:
观察者模式
Castle动态代理