好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

.NET面向上下文、AOP架构模式(实现)

.NET面向上下文、AOP架构模式(实现)

1.   上下文概述

上下文:其实就是一个逻辑上的业务、功能区域。在这个逻辑区域里可以有效的进行管理,算是一种制度的约束,也可以理解为某种范围类的数据共享 。

其实在很多应用框架中到处可以看见上下文的概念,包括.NET本身的设计就建立在这种思想上的。实例化的对象默认存在于系统中的默认上下文中,我们可以构建自己的上下文将对象在运行时进行合理的管理。

在ASP.NET框架中比较经典的就是 HttpContext 上下文对象。所有的运行时对象都会逻辑归属到 HttpContext 上下文中来,如:我们可以使用Request 、Response 等对象访问HTTP处理的生命周期数据。

在Remoting中跨 AppDomin访问也是建立在上下文基础上的,请求的消息通过隧道后序列化到达调用发。 王清培版权所有,转载请给出署名

在这些强大的应用框架背后总有着让人难以琢磨的设计秘方,诸多的设计原则、设计模式、丰富的实践经验都将是框架稳定运行的基石。Context算是一个比较完美的逻辑范围设计模式。 [王清培版权所有,转载请给出署名]

那么就让我们来领略一下上下文的奥秘吧!

2.   上下文的一般应用

上下文的设计思想绝对的美妙,很多地方一旦进行上下文抽象就能解决很多问题。比如在Remoting中我们可以动态的在上下文中加入很多扩展对上下文中的所有对象进行强制管理,比如:调用某一个方法我们需要进行安全检查,我们可以编写一个满足自己当前项目需求的安全认证插件动态的注入到上下文管理器区域中,在这个地方就体现出上下文的设计优势。

在Web 编程中,由于它有着与 Winfrom 编程很大的差异性,需要将同一组对象同时服务于 N 个客户端进行使用,而在 Winfrom 中基本上都是属于单线程的,当然可以手动的开启多线程并行操作。对于 ASP.NET每当有新的请求处理时,框架会自动开启新的线程去处理当前的调用,然后这个时候就是需要一个相对于之前操作的独立上下文数据环境,而不是在同一个服务器上的所有线程都是共享的。 王清培版权所有,转载请给出署名

那么我们就需要将当前的HTTP处理的相关数据纳入到一个逻辑的上下文进行管理和数据共享。

这么多的优势存在,看来我们是有必要尝试一下这中设计模式了。那么就目前系统开发框架而言我们的上下文能用在哪里呢?我想当务之急就是将分层架构中的所有单条线上的对象进行上下文管理。 [王清培版权所有,转载请给出署名]

典型的三层架构:

在一般的三层架构开发过程中我们的调用关系基本都是这样的,利用上下文设计模式我们可以将本来鼓励的对象进行合理的管理。上图中 User 对象线将是属于 User 上下文的, Order 对象线将是属于 Order 上下文的。大家互不干扰,可以在这个逻辑上下文中共享数据、设置调用安全策略、设计日志记录方式、甚至可以计算每个方法的性能。

BLL 的调用代码:

View Code

DAL的执行代码:

View Code

上述代码是我模拟一个上下文的执行过程。

3.   上下文共享区域

在每个独立的上下文环境中应该有一片共享的数据存储区域,以备多个上下文对象访问。这种方便性多半存在于项目比较紧张的修改需求的时候或者加新业务的时候扩展方法用的。 BLL 调用代码:

View Code

DAL执行代码:

View Code

4.   上下文运行时环境

对于上下文运行时环境的构建需要考虑到运行时是共享的上下文对象。对于纳入上下文管理的所有对象都需要共享或者说是受控于上下文运行时。

上下文构建:

View Code

对于上下文的入口构建:

View Code

通过 Using 的方式我们开始上下文生命周期。

5.   上下文活动对象

上下文对象的绑定需要延后,不能在对象的构建时就创建上下文。

使用后期绑定动态的切入到执行的上下文中。

调用代码,上下文入口:

View Code

6.   上下文在分层架构中的运用

有了上下文的核心原型之后我们可以扩展到分层架构中来,对于分层架构的使用其实很有必要,一般的大型业务系统都是混合的使用模式,可能有 C/S 、 B/S 、 Mobile 终端等等。

对于加入 Service 层之后 BLL 、 DAL 将位于服务之后,对于来自客户端的调用需要经过一些列的身份验证及权限授予。有了 WCF 之后面向 SOA 的架构开发变的相对容易点,对安全、性能、负载等等都很完美,所以大部分的情况下我们很少需要控制 BLL 、 DAL 的执行运行。

那么没有使用 WCF 构建分布式的系统时或者是没有分布式的需求就是直接的调用,如 WEB 的一般开发,从 UI 到 BLL 到 DAL 。或者是普通的 Winfrom 的项目、控制台项目属于内网的使用,可能就需要控制到代码的执行。

下面我通过演示一个具体的实例来看看到底效果如何。

我以控制台的程序作为演示项目类型,也使用简单的三层架构。

这个再简单不过了吧,为了演示越简单越好,关键是突出重点。

需求:

在 DAL 对象里面加入一个插入 Order 实体对象的方法:

View Code

在这个类的上面有一个特性 ContextEveningBound , 该是用来表示当前对象属于后期绑定到上下文的对象。同时该类也继承自一个 ContextModuleBaseObject< DAL_Order> 泛型类,主要作用是将对象强制的绑定到上下文进行管理。

在方法InsertOrderSingle上面有两个特性,ContextLogHandler是用来记录方法的执行日志,ContextSecurityHanlder是用来在方法执行的过程中强制要求管理员认证。

BLL 对象代码:

View Code

在BLL对象里面有一个调用DAL对象方法的实例对象,为了演示简单这里没有加入层的依赖注入设计方案,通过直接调用方式。在BLL方法体中有一个专门用来在上下文中调用方法的接口,这是约束目的是为了能让框架切入到方法的执行之前先执行。具体的设计原理我将在下一篇文章中详细讲解。

在方法的上面有一个ContextExceptionHandler特性,目的是安全的调用DAL对象的方法,在有异常的情况下能通过上下文的方式人性化的提示错误信息。这样我们就不需要频繁的编写捕获异常的代码,看起来也不爽,我们要的是代码的整洁、美丽。

UI 调用:

View Code

执行效果:

会先执行日志的记录,然后要求我们输入用户凭证才能继续执行下面的方法。

我输入YES才能继续执行插入的方法。我们可以通过很简单的实现上下文的管理接口,对方法进行控制。

总结:该篇文章只是介绍上下文的作用、原理、优势。下篇文章:“.NET 面向上下文架构模式(实现)”将详细的介绍上下文框架如何开发。 [王清培版权所有,转载请给出署名]

 

分类:  企业架构、设计模式 ,  软件工程 ,  系统架构设计 ,  应用框架设计 ,  组件、中间件开发

1.上下文Context、面向切面编程AOP模型分析

在本人的 .NET面向上下文、AOP架构模式(概述) 一文中,我们大概了解了上下文如何辅助对象在运行时的管理。在很多时候我们急需在运行时能把对象控制在一定的逻辑范围内,在必要的时候能让他们体现出集中化的概念,如人群、车辆、动物等等。而Context与AOP有着密切的联系,Context表示逻辑抽象的范围而AOP描述了在这个逻辑范围内如何进行控制。其实这两者都是设计模式外的设计模式,与具体的技术实现无关。 [ 王清培版权所有,转载请给出署名]

那么Context与AOP两者在逻辑上是一个怎样的概念呢?似乎只有图才能最贴切的表达人的理解思路。下图展现Context与AOP紧密合作的概念模型。

对象在运行时被上下文管理,在上下文中可以很方便的获取到所有的受管理的对象,这为后面的AOP做了铺垫。只有Context启动后AOP管理器的爪子才能伸进对象的运行时内部与AOP的连接点建立起通讯关系,才能真正的使对象能面向切面成功。 [王清培版权所有,转载请给出署名]

在模型图中,Context中心负责对所有Object进行管理,而Object体现出多面性属性、多面性行为都将包括多面性的特点,通过与AOP管理器进行连接将控制对象的运行时行为。 [王清培版权所有,转载请给出署名]

通过合理的约定对象的AOP抽象接口,尽可能的最大化将控制权移动到客户所实现的“面”中去。比如对某类方法的调用,可能需要尽可能的控制方法的所有执行权。所以对具体的抽象定义有很大的难度。

在上图中,我们通过AOP核心管理器与对象的“面”连接上,根据具体的“面”类型进行动态调用,比如属性,可能需要在运行时根据业务环节进行呈现、动态绑定等等,都可以通过AOP去实现。对于方法,可能在面向SOA的架构中需要记录下当前客户端的调用信息,还有一些独特的业务认证等等。不同的“面”需要进行逻辑抽象,这也符合面向对象的设计原则。很多时候我们需要先“约定”而不是尽可能的提供扩展的机制,扩展点越多系统的复杂程度就越大,相对也就难以控制。

 

2.上下文的实现

对上下文、AOP模型我们大致分析了一下,通过模型图也很形象的体现出上下文、AOP的主要的工作原理。下面我们来通过具体的分析来搭建物理的代码模型。

面向对象是对抽象的逻辑模型进行物理性的建模,能否真正的体现出模型,需要我们细心的分析才行。 [王清培版权所有,转载请给出署名]

 

2.1 上下文模型实现

我们对上下文模型进行抽象实现为代码模型。那么我们需要一个逻辑上代表上下文的对象,在这里我将它命名为ContextRuntime,上下文的生命周期控制在Using()语句的代码段中,那么ContextRuntime需要实现Idisposable接口,让Using()语句能起作用。

using (ContextModule. ContextRuntime.BeginContextRuntime())

{    // 上下文的生命周期

}

为什么要这样设计上下文的生命周期呢,这样设计是最为灵活的。在诸如很多微软的内部上下文生命周期的入口也是这样设计的,最经典的就是System.Transaction事务处理。

当Using()代码段结束后,我们需要释放当前上下文实例,所以我们需要完成IDisposable接口的实现。

void  IDisposable.Dispose()

{

_currentContextRuntime =  null;

}

_  currentContextRuntime表示上下文对象实例的全局私有对象。

由于多线程应用框架的入口点不是我们所能控制的,所以在使用上下文模式的时候需要使用线程本地存储解决线程不安全访问的问题。

[ ThreadStatic]

private  static ContextRuntime _currentContextRuntime;

我们看一下全部的ContextRuntime对象代码:

View Code

这里只为了实现基本的模型原型,不会涉及太多的功能。上下文主要是在当前线程中开启,然后保持在静态对象的多线程安全访问,最后就是对象的稳定释放。

2.2 上下文对象绑定实现

有了上下文之后,如何才能使对象在运行时动态的绑定到上下文中来。这个需要在前期编码的时候就确定对象是否要绑定到当前上下文以便进行管理。

那么我们需要对客户使用的对象进行一个抽象,让所有需要绑定的对象实现我们高层定义的抽象。这里我将命名为ContextModuleBaseObject,由于需要向AOP提供对象的“面”的连接点,所以我们需要在运行时反射获取到绑定对象的一些基本信息,如:属性的“面”、行为的“面”、包括对象本身的“面”,这些面我们需要将其对应关系建立起来才能让后面的AOP起作用。

所以我们将ContextModuleBaseObject定义为泛型类,并且需要加上Class的约束。对于绑定的对象在运行时一旦进入上下文的生命周期管理,就需要灵活的表示为ContextRuntime对象,这样设计符合上下文一视同仁的观念,更便于ContextModuleBaseObject对象在运行期动态的使用上下文的内部存储区域。

这里我们需要将ContextModuleBaseObject对象继承自ContextRuntime对象。使用经典的装饰者模式,让ContextModuleBaseObject对象使用ContextRuntime行为。

View Code

ContextModuleBaseObject 类主要实现的功能就是将对象动态的添加到当前上下文中。然后为AOP做些辅助性的工作,包括对类、属性、行为的特性元数据的缓存,这里只实现了行为的特性缓存。可以根据自己的需要扩展AOP的功能,在对象的属性上标记特性让属性也发挥作用。这里的特性就是AOP公布的指定接口。

对_JoinOperation方法的解释我们留在后面,这里是一个数据结构。将ContextOperationBaseAttribute

类型串成链表,让方法的执行穿过所有的ContextOperationBaseAttribute处理类。

2.3 上下文对象的后期绑定实现

为了让绑定对象支持上下文后期绑定,需要一个特性作为表示。

View Code

仅仅为了标识后期绑定说明。在ContextModuleBaseObject 对象的构造函数中可以看到。 [王清培版权所有,转载请给出署名]

View Code

到这里我们已经实现对象的动态绑定到上下文来,下面我们来分析Context如何用AOP配合完成面向切面编程的机制。

2.4.AOP 中的对象行为的契约设计实现

其实这里的契约设计也就是图2中对AOP中的“面”的约定。

AOP全称为“面向切面编程”。对象在运行时具备多个面,其实在.NET里面我们习惯性的用特性(Attribute)来表达这个概念。因为不需要改动任何代码就可以将特性加到对象中的任何元素中去,在不同的业务环节或者说是功能环节就能动态的转动元素体现出“切面”的优势,当然具体的实现可能很多种,这里使用特性来完成。

在此约定任何处理对象方法的“面”都将被抽象。这里我将命名为ContextOperationBaseAttribute该特性表示所有附加到方法上的特性的基类,对“面”的抽象。

那么不同类型的面将有着不同的操作行为,比如:记录日志的特性、计算性能的特性、认证安全的特性他们都有着不同的行为和属性,所以这里我们还需要提取一个顶层接口作为行为类的特性处理抽象。这里我将命名为IContextOperationHandler该接口作为统一执行行为特性的高层依赖。其实这里也体现出依赖倒置原则,依赖抽象不依赖具体实现。

完整的ContextOperationBaseAttribute类:

View Code

作为抽象的顶层类需要完成派生类重复的劳动。这里实现了一个ResultAction泛型方法,该方法是外部调用绑定对象的方法时的入口点。但是具体的实现区别在于IContextOperationHandler 接口的定义。

由于行为的特性可能存在多个,所以对于最后一个处理完的特性需要结束整个的调用链表,并且返回值。在ResultAction虚方法里面对IContextOperationHandler 接口的Operation方法执行调用,该方法将会在实现特定行为特性里面实现,这里又体现出“模板方法”设计模式。在抽象类中约定行为,在派生类中实现具体。

这里比较有意思的是,特性不在像大家实现ORM的那中简单的标识了。其实特性真正强大的地方在于运行时能动态的获取到,这得益于.NET元数据的功劳。并且动态实例化然后当作普通的对象实例使用。这个观念很多.NET程序员不宜转变。

在这里的ContextOperationBaseAttribute又描述了另外一种数据结构“单向链表”,为了将绑定对象的行为最大化的在特性中实现,我们将方法的调用完全的传递到实现特性中去。那么对方法上多个作用的特性如何穿过呢,并且能保证数据的正常传递和返回。有两点我们需要注意,一个是特性的作用顺序,二个是特性对方法的执行是否完成。这两点我们都要考虑进去,所以在ContextOperationBaseAttribute类中用public  int OperationSort { get; set; } 属性表示特性的执行顺序,记录日志的特性和计算性能的特性我们很难在这里定死,需要根据后期程序的执行情况而定,如我要先记录日志然后在执行方法。

那么我们又如何将ContextOperationBaseAttribute类型串联起来呢?在ContextModuleBaseObject

泛型绑定类中我们在构造的时候就将通过ContextOperationBaseAttribute.  OperationSort 属性初始化了特性处理链表。

那么我们如何将具体的对象与特性关联建立起对应关系呢?一个行为可能有多个ContextOperationBaseAttribute的实现。所以这里我们需要一个能满足行为与特性之间的数据结构。

这里我将它定义为ContextFilterHandlerMap该类继承自Dictionary< string,ContextOperationBaseAttribute> 泛型字典类,使用KEY—VALUE的方式存放行为与ContextOperationBaseAttribute处理特性的对应关系。

View Code

最后只需要向外提供IContextOperationHandler接口就可以实现方法与处理特性的串联了。

View Code

通过对外公开接口,让实现“面”的客户端去完成对具体对象方法的执行。ContextMethodInfo类型是包装System.Reflection. MethodInfo 方法元数据的,将通过调用切入到方法内部。 [王清培版权所有,转载请给出署名]

这里基本上实现了AOP对行为的多面支持,下面我们来看一下如果动态的切入到方法中。

2.5. 动态入口的实现

对所有方法的调用将是比较头疼的。由于一般面向上下文、面向切面都是有编写者控制对方法的调用,可以很方便的通过后台的隐式的调用。但是作为普通的方法的入口调用主要有三种方式实现。

1): 委托实现入口

通过使用System.Delegate动态派生类型来完成对方法的调用,但是委托对于方法的签名必须是强类型的,所以很难做到通用的调用入口。

2): 反射实现入口 ( 通过扩展方法在 OBJECT 基类中加入获取 MethodInfo 对象的方法,使用时通过该方法获取调用方法的信息 )

 

通过扩展方法在System.Object中加入一个扩展方法用来获取调用方法的信息,然后通过反射动态的调用,这种方法只比较常用的。但是如何框架是在.NET2.0中使用的扩展方法还不能实现,这里我是在ContextModuleBaseObject基类中加了一个类似扩展方法的方式。绑定对象可以很方便的获取到调用方法的MethodInfo对象。

 

3): 完美的动态编译 ( 向抽象、多态敬礼 )

最为完美的是扩展代码生成提供程序,在使用的对象里面在派生一个类,专门用来进行多态的转移,让高层的调用顺利进入到派生的类中。不过比较复杂。

这里是使用第二种方式使用的:

View Code

所有的调用均使用PostMethod泛型方法启动。_LockPostObejctIsEveningBound私有方法,判断当前类型是否是后期绑定,如果是则需要切入到基类中调用_ EveningBoundChildClass方法进行ContextOperationBaseAttribute 类型的链表构造,然后直接通过头对象进行调用。 [王清培版权所有,转载请给出署名]

3 . 实例上下文与静态上下文

对于实例上下文同时也就存在静态上下文的概念,对于静态对象的逻辑归纳有点难度,由于静态对象在面向对象设计方面很难抽象。只能通过特性注入的方式强制性的将静态对象拉入上下文。但是在多线程的情况下,确实是可以研究的。将静态对象全部进行线程本地存储,强制性的进行类似实体对象的管理。

4.面向上下文的领域模型(DMM)

 

基于上下文的使用模式可以进行领域模型的初步构造,可以先向领域中的大比例结构靠近,将业务模型逻辑归纳到一定的Context中,对业务的模块化梳理也是一种实现。

在分层架构中的业务逻辑层可能需要加入上下文的管理,将业务模型进行运行时控制。比如订单处理,将订单业务流程相关的模型对象归纳为一块。比如用户相关,将用户管理的业务流程相关的模型对象归纳为一块,确实是很有意思。

 

源码地址: https://files.cnblogs.com/wangiqngpei557/ContextModuleDemo.zip

 

分类:  调试框架 ,  企业架构、设计模式 ,  系统架构设计 ,  应用框架设计 ,  组件、中间件开发

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于.NET面向上下文、AOP架构模式(实现)的详细内容...

  阅读:52次