c#

位置:IT落伍者 >> c# >> 浏览文章

.NET面向上下文、AOP架构模式


发布日期:2024年02月06日
 
.NET面向上下文、AOP架构模式

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

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

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

Context图

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

在模型图中Context中心负责对所有Object进行管理而Object体现出多面性属性多面性行为都将包括多面性的特点通过与AOP管理器进行连接将控制对象的运行时行为

AOP图

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

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

上下文的实现

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

面向对象是对抽象的逻辑模型进行物理性的建模能否真正的体现出模型需要我们细心的分析才行

上下文模型实现

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

using (ContextModuleContextRuntimeBeginContextRuntime())

{ //上下文的生命周期

}

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

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

void IDisposableDispose()

{

_currentContextRuntime = null;

}

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

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

[ThreadStatic]

private static ContextRuntime _currentContextRuntime;

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

namespace ContextModule

{

/// <summary>

/// 上下文运行时环境

/// 上下文逻辑运行时环境环境中的功能都是可以通过附加进来的

/// </summary>

public class ContextRuntime : IDisposable

{

#region IDisposable成员

void IDisposableDispose()

{

_currentContextRuntime = null;

}

#endregion

protected ContextRuntime() { }

private DateTime _initTime = DateTimeNow;

/// <summary>

/// 获取运行时创建上下文的时间

/// </summary>

public virtual DateTime InitTime { get { return _initTime; } }

private Dictionary<object object> _runTimeResource = new Dictionary<object object>()

private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap()

/// <summary>

/// 获取上下文中的方法类过滤器映射表

/// </summary>

public ContextFilterHandlerMap FilterMap { get { return _filterMap; } }

private Guid _initPrimaryKey = GuidNewGuid()

/// <summary>

/// 获取运行时创建上下文的唯一标识

/// </summary>

public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } }

/// <summary>

/// 获取上下文共享区域中的数据

/// </summary>

/// <param name=key>数据Key</param>

/// <returns>object数据对象</returns>

public virtual object GetValue(object key)

{

return _runTimeResource[key];

}

/// <summary>

/// 设置上下文共享区域中的数据

/// </summary>

/// <param name=key>数据Key</param>

/// <param name=value>要设置的数据对象</param>

public virtual void SetValue(object key object value)

{

_runTimeResource[key] = value;

}

[ThreadStatic]

private static ContextRuntime _currentContextRuntime;

/// <summary>

/// 获取当前上下文运行时对象

/// </summary>

public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } }

/// <summary>

/// 开始运行时上下文

/// </summary>

/// <returns>ContextRuntime</returns>

public static ContextRuntime BeginContextRuntime()

{

//可以通过配置文件配置上下文运行时环境的参数这里只是实现简单的模拟

_currentContextRuntime = new ContextRuntime()

return _currentContextRuntime;

}

}

}

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

上下文对象绑定实现

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

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

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

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

using SystemReflection;

namespace ContextModule

{

/// <summary>

/// 上下绑定基类强制派生类绑定到上下文

/// 逻辑上下文的策略构造都在这里进行

/// </summary>

/// <typeparam name=T>受管理的上下文绑定对象类型通常是ContextModuleBaseObject派生类</typeparam>

public class ContextModuleBaseObject<T> : ContextRuntime where T : class

{

/// <summary>

/// 当前上下文绑定对象所绑定到的上下文物理对象实例

/// </summary>

private ContextRuntime _contextRunTime;

public ContextModuleBaseObject()

{

if (typeof(T)GetCustomAttributes(typeof(ContextEveningBoundAttribute) false) != null)

{

_IsEvening = true;

return;

}

//前期静态绑定上下文

if (ContextRuntimeCurrentContextRuntime == null)

throw new Exception(上下文环境未能初始化请检查您的代码入口是否启用了ContextRuntime对象

_contextRunTime = ContextRuntimeCurrentContextRuntime;

_InitContextHandler<T>()

}

/// <summary>

/// 构造上下文的类过滤器方法过滤器映射表

/// </summary>

private void _InitContextHandler<ChildType>() where ChildType : class

{

//构造类过滤器

ContextOperationBaseAttribute[] classattr =

typeof(ChildType)GetCustomAttributes(typeof(ContextOperationBaseAttribute) false) as ContextOperationBaseAttribute[];

if (classattrLength >

{

ContextOperationBaseAttribute joinoper = _JoinOperation(classattr)

_contextRunTimeFilterMapMapOperation(typeof(T)FullName joinoper)

}

//构造方法过滤器

foreach (MethodInfo method in typeof(ChildType)GetMethods())

{

ContextOperationBaseAttribute[] methodattr =

methodGetCustomAttributes(typeof(ContextOperationBaseAttribute) false) as ContextOperationBaseAttribute[];

if (methodattrLength <=

continue;

ContextOperationBaseAttribute joinoper = _JoinOperation(methodattr)

_contextRunTimeFilterMapMapOperation(stringFormat({}{} methodDeclaringTypeFullName methodName) joinoper)

}

}

internal bool _IsEvening { get; set; }

/// <summary>

/// 后期动态绑定上下文

/// </summary>

internal void _EveningBoundChildClass<ChildType>() where ChildType : class

{

if (_contextRunTime != null)

return;//说明之前已经进行过动态调用

_contextRunTime = ContextRuntimeCurrentContextRuntime;//动态绑定当前运行时上下文

_InitContextHandler<ChildType>()

}

private ContextOperationBaseAttribute _JoinOperation(ContextOperationBaseAttribute[] operationarray)

{

//必须对数组进行排序后才能连接

for (int i = ; i < operationarrayLength; i++)

{

for (int j = ; j < i; j++)

{

if (operationarray[j]OperationSort > operationarray[j + ]OperationSort)

{

ContextOperationBaseAttribute oper = operationarray[j];

operationarray[j] = operationarray[j + ];

operationarray[j + ] = oper;

}

}

}

ContextOperationBaseAttribute opernext = operationarray[];

for (int i = ; i < operationarrayLength; i++)

{

opernextNextOperation = operationarray[i];

opernext = operationarray[i];//保持对当前循环对象的上级对象的引用

}

return operationarray[];

}

public MethodInfo GetMethodInfo(string methodname)

{

return thisGetType()GetMethod(methodname)

}

public override Guid InitPrimaryKey

{

get

{

return _contextRunTimeInitPrimaryKey;

}

}

public override DateTime InitTime

{

get

{

return _contextRunTimeInitTime;

}

}

public override object GetValue(object key)

{

return _contextRunTimeGetValue(key)

}

public override void SetValue(object key object value)

{

_contextRunTimeSetValue(key value)

}

}

}

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

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

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

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

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

namespace ContextModule

{

/// <summary>

/// 确定设置类是否需要后期动态绑定到上下文

/// 使用该特性的类将是上下文活跃的只有在使用的时候才确定当前上下文

/// </summary>

[AttributeUsage(AttributeTargetsClass AllowMultiple = false Inherited = true)]

public class ContextEveningBoundAttribute : Attribute

{

public ContextEveningBoundAttribute() { }

private bool _isEvening;

/// <summary>

/// 指定对象是否需要后期动态绑定上下文

/// </summary>

public bool IsEvening { set { _isEvening = value; } get { return _isEvening; } }

}

}

仅仅为了标识后期绑定说明在ContextModuleBaseObject 对象的构造函数中可以看到

[csharp]

public ContextModuleBaseObject()

{

if (typeof(T)GetCustomAttributes(typeof(ContextEveningBoundAttribute) false) != null)

{

_IsEvening = true;

return;

}

//前期静态绑定上下文

if (ContextRuntimeCurrentContextRuntime == null)

throw new Exception(上下文环境未能初始化请检查您的代码入口是否启用了ContextRuntime对象

_contextRunTime = ContextRuntimeCurrentContextRuntime;

_InitContextHandler<T>()

}

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

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

其实这里的契约设计也就是图中对AOP中的的约定

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

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

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

完整的ContextOperationBaseAttribute 类

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

namespace ContextModule

{

/// <summary>

/// 上下文操作动作特性化基类

/// 所有对上下文中的类方法的过滤操作都必须继承此类

/// </summary>

public abstract class ContextOperationBaseAttribute : Attribute IContextOperationHandler

{

/// <summary>

/// 过滤器的处理顺序从小到大的方式进行处理

/// </summary>

public int OperationSort { get; set; }

/// <summary>

/// 下一次过滤操作类

/// </summary>

internal ContextOperationBaseAttribute NextOperation { get; set; }

/// <summary>

/// 开始处理过滤对象

/// </summary>

/// <typeparam name=Result>方法的返回值类型</typeparam>

/// <param name=actionmethod>调用的方法包装</param>

/// <param name=paramarray>方法的有序参数</param>

/// <returns></returns>

public virtual Result ResultAction<Result>(ContextMethodInfo actionmethod params object[] paramarray)

{

object result = null;

if (!actionmethodIsPost)

{

result = (this as IContextOperationHandler)Operation(actionmethod paramarray)

if (thisNextOperation != null)

return thisNextOperationResultAction<Result>(actionmethod paramarray)

}

if (result != null)

return (Result)result;

return default(Result)

}

public abstract object Operation(ContextMethodInfo contextmethod params object[] paramarray)

}

}

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

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

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

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

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

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

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

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

namespace ContextModule

{

/// <summary>

/// 特定于上下文的过滤器映射表

/// 上下文中的任何方法如果需要进行上下文管理的则使用ContextModuleContextOperationBaseAttribute特性派生类进行管理

/// 所有附加于方法类上的特性管理类都将被映射到ContextModuleContextFilterHandlerMap实例中

/// </summary>

public class ContextFilterHandlerMap : Dictionary<string ContextOperationBaseAttribute>

{

public ContextFilterHandlerMap() { }

/// <summary>

/// 获取方法对应的过滤器处理特性

/// </summary>

/// <param name=mapname>映射Key</param>

/// <returns>ContextOperationBaseAttribute特性实例</returns>

public ContextOperationBaseAttribute MapOperation(string mapname)

{

return this[mapname];

}

/// <summary>

/// 设置过滤器与特定方法的映射

/// </summary>

/// <param name=mapname>映射Key</param>

/// <param name=operationlist>过滤器特性基类ContextOperationBaseAttribute</param>

public void MapOperation(string mapname ContextOperationBaseAttribute operationlist)

{

thisAdd(mapname operationlist)

}

}

}

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

using SystemIO;

namespace ContextModule

{

/// <summary>

/// 上下文操作管理接口

/// </summary>

public interface IContextOperationHandler

{

/// <summary>

/// 开始上下文处理

/// </summary>

/// <param name=contextmethod>CRL目前正在执行的上下文方法的信息

/// 可以通过ContextMethodInfo实例获取方法详细信息</param>

///<param name=paramarray>参数数组</param>

object Operation(ContextMethodInfo contextmethod params object[] paramarray)

}

}

通过对外公开接口让实现的客户端去完成对具体对象方法的执行ContextMethodInfo 类型是包装SystemReflection MethodInfo方法元数据的将通过调用切入到方法内部

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

动态入口的实现

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

委托实现入口

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

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

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

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

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

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

[csharp]

/***

* author:深度训练

* blog:

* **/

using System;

using SystemCollectionsGeneric;

using SystemText;

using SystemReflection;

namespace ContextModule

{

/// <summary>

/// 面向上下文的操作类

/// 对上下文发起的方法调用需要通过该基类进行调用才能让我们的扩展点使用成为可能

/// </summary>

public static class ContextAction

{

/// <summary>

/// 在面向上下文的环境中进行方法的调用

/// </summary>

/// <typeparam name=PostObjectType>调用的上下文绑定对象类型</typeparam>

/// <typeparam name=ResultType>方法的返回类型</typeparam>

/// <param name=post>调用的上下文绑定对象的实例</param>

/// <param name=method>方法的信息对象MethodInfo通过OjectGetContextMethodInfo方法自动获取</param>

/// <param name=paramarray>方法的有序参数集合</param>

/// <returns>ResultType泛型类型指定的返回实例</returns>

public static ResultType PostMethod<PostObjectType ResultType>(PostObjectType post MethodInfo method params object[] paramarray)

where PostObjectType : ContextModuleBaseObject<PostObjectType>

{

_LockPostObejctIsEveningBound<PostObjectType>(post)

string key = stringFormat({}{} methodDeclaringTypeFullName methodName)

if (!ContextRuntimeCurrentContextRuntimeFilterMapContainsKey(key))

{

throw new Exception(stringFormat(方法{}未经过上下文进行管理 key))

}

ContextMethodInfo contextmethod = new ContextMethodInfo(method post)

return ContextRuntimeCurrentContextRuntimeFilterMap[key]ResultAction<ResultType>(contextmethod paramarray)

}

/// <summary>

/// 检查调用实例类是否属于后期绑定

/// 通过使用ContextModuleContextEveningBound(IsEvening = true)方式指定后期绑定上下文

/// </summary>

private static void _LockPostObejctIsEveningBound<PostObjectType>(PostObjectType post)

where PostObjectType : ContextModuleBaseObject<PostObjectType>

{

ContextModuleBaseObject<PostObjectType> contextclass = post as ContextModuleBaseObject<PostObjectType>;

if (contextclass_IsEvening)

contextclass_EveningBoundChildClass<PostObjectType>()

}

}

}

所有的调用均使用PostMethod泛型方法启动_LockPostObejctIsEveningBound私有方法判断当前类型是否是后期绑定如果是则需要切入到基类中调用_ EveningBoundChildClass方法进行ContextOperationBaseAttribute 类型的链表构造然后直接通过头对象进行调用

实例上下文与静态上下文

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

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

基于上下文的使用模式可以进行领域模型的初步构造可以先向领域中的大比例结构靠近将业务模型逻辑归纳到一定的Context中对业务的模块化梳理也是一种实现[王清培版权所有转载请给出署名]

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

               

上一篇:C#实现远程线程插入

下一篇:.NET中的Parameters