当前位置: 首页 > 编程日记 > 正文

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

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

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

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

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

Context图:1

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

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

通过合理的约定对象的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对象代码:
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. namespace ContextModule
  9. {
  10. /// <summary>
  11. /// 上下文运行时环境。
  12. /// 上下文逻辑运行时环境,环境中的功能都是可以通过附加进来的。
  13. /// </summary>
  14. public class ContextRuntime : IDisposable
  15. {
  16.         #region IDisposable成员
  17. void IDisposable.Dispose()
  18. {
  19. _currentContextRuntime = null;
  20. }
  21.         #endregion
  22. protected ContextRuntime() { }
  23. private DateTime _initTime = DateTime.Now;
  24. /// <summary>
  25. /// 获取运行时创建上下文的时间
  26. /// </summary>
  27. public virtual DateTime InitTime { get { return _initTime; } }
  28. private Dictionary<objectobject> _runTimeResource = new Dictionary<objectobject>();
  29. private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap();
  30. /// <summary>
  31. /// 获取上下文中的方法、类过滤器映射表
  32. /// </summary>
  33. public ContextFilterHandlerMap FilterMap { get { return _filterMap; } }
  34. private Guid _initPrimaryKey = Guid.NewGuid();
  35. /// <summary>
  36. /// 获取运行时创建上下文的唯一标识
  37. /// </summary>
  38. public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } }
  39. /// <summary>
  40. /// 获取上下文共享区域中的数据
  41. /// </summary>
  42. /// <param name="key">数据Key</param>
  43. /// <returns>object数据对象</returns>
  44. public virtual object GetValue(object key)
  45. {
  46. return _runTimeResource[key];
  47. }
  48. /// <summary>
  49. /// 设置上下文共享区域中的数据
  50. /// </summary>
  51. /// <param name="key">数据Key</param>
  52. /// <param name="value">要设置的数据对象</param>
  53. public virtual void SetValue(object key, object value)
  54. {
  55. _runTimeResource[key] = value;
  56. }
  57. [ThreadStatic]
  58. private static ContextRuntime _currentContextRuntime;
  59. /// <summary>
  60. /// 获取当前上下文运行时对象.
  61. /// </summary>
  62. public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } }
  63. /// <summary>
  64. /// 开始运行时上下文
  65. /// </summary>
  66. /// <returns>ContextRuntime</returns>
  67. public static ContextRuntime BeginContextRuntime()
  68. {
  69. //可以通过配置文件配置上下文运行时环境的参数。这里只是实现简单的模拟。
  70. _currentContextRuntime = new ContextRuntime();
  71. return _currentContextRuntime;
  72. }
  73. }
  74. }

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

2.2上下文对象绑定实现

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

那么我们需要对客户使用的对象进行一个抽象,让所有需要绑定的对象实现我们高层定义的抽象。这里我将命名为ContextModuleBaseObject,由于需要向AOP提供对象的“面”的连接点,所以我们需要在运行时反射获取到绑定对象的一些基本信息,如:属性的“面”、行为的“面”、包括对象本身的“面”,这些面我们需要将其对应关系建立起来才能让后面的AOP起作用。
所以我们将ContextModuleBaseObject定义为泛型类,并且需要加上Class的约束。对于绑定的对象在运行时一旦进入上下文的生命周期管理,就需要灵活的表示为ContextRuntime对象,这样设计符合上下文一视同仁的观念,更便于ContextModuleBaseObject对象在运行期动态的使用上下文的内部存储区域。
这里我们需要将ContextModuleBaseObject对象继承自ContextRuntime对象。使用经典的装饰者模式,让ContextModuleBaseObject对象使用ContextRuntime行为。
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.Reflection;
  9. namespace ContextModule
  10. {
  11. /// <summary>
  12. /// 上下绑定基类,强制派生类绑定到上下文。
  13. /// 逻辑上下文的策略构造都在这里进行。
  14. /// </summary>
  15. /// <typeparam name="T">受管理的上下文绑定对象类型,通常是ContextModuleBaseObject派生类。</typeparam>
  16. public class ContextModuleBaseObject<T> : ContextRuntime where T : class
  17. {
  18. /// <summary>
  19. /// 当前上下文绑定对象所绑定到的上下文物理对象实例。
  20. /// </summary>
  21. private ContextRuntime _contextRunTime;
  22. public ContextModuleBaseObject()
  23. {
  24. if (typeof(T).GetCustomAttributes(typeof(ContextEveningBoundAttribute), false) != null)
  25. {
  26. _IsEvening = true;
  27. return;
  28. }
  29. //前期静态绑定上下文
  30. if (ContextRuntime.CurrentContextRuntime == null)
  31. throw new Exception("上下文环境未能初始化,请检查您的代码入口是否启用了ContextRuntime对象。");
  32. _contextRunTime = ContextRuntime.CurrentContextRuntime;
  33. _InitContextHandler<T>();
  34. }
  35. /// <summary>
  36. /// 构造上下文的类过滤器、方法过滤器映射表。
  37. /// </summary>
  38. private void _InitContextHandler<ChildType>() where ChildType : class
  39. {
  40. //构造类过滤器
  41. ContextOperationBaseAttribute[] classattr =
  42. typeof(ChildType).GetCustomAttributes(typeof(ContextOperationBaseAttribute), falseas ContextOperationBaseAttribute[];
  43. if (classattr.Length > 0)
  44. {
  45. ContextOperationBaseAttribute joinoper = _JoinOperation(classattr);
  46. _contextRunTime.FilterMap.MapOperation(typeof(T).FullName, joinoper);
  47. }
  48. //构造方法过滤器
  49. foreach (MethodInfo method in typeof(ChildType).GetMethods())
  50. {
  51. ContextOperationBaseAttribute[] methodattr =
  52. method.GetCustomAttributes(typeof(ContextOperationBaseAttribute), falseas ContextOperationBaseAttribute[];
  53. if (methodattr.Length <= 0)
  54. continue;
  55. ContextOperationBaseAttribute joinoper = _JoinOperation(methodattr);
  56. _contextRunTime.FilterMap.MapOperation(string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name), joinoper);
  57. }
  58. }
  59. internal bool _IsEvening { getset; }
  60. /// <summary>
  61. /// 后期动态绑定上下文。
  62. /// </summary>
  63. internal void _EveningBoundChildClass<ChildType>() where ChildType : class
  64. {
  65. if (_contextRunTime != null)
  66. return;//说明之前已经进行过动态调用
  67. _contextRunTime = ContextRuntime.CurrentContextRuntime;//动态绑定当前运行时上下文
  68. _InitContextHandler<ChildType>();
  69. }
  70. private ContextOperationBaseAttribute _JoinOperation(ContextOperationBaseAttribute[] operationarray)
  71. {
  72. //必须对数组进行排序后才能连接
  73. for (int i = 0; i < operationarray.Length; i++)
  74. {
  75. for (int j = 0; j < i; j++)
  76. {
  77. if (operationarray[j].OperationSort > operationarray[j + 1].OperationSort)
  78. {
  79. ContextOperationBaseAttribute oper = operationarray[j];
  80. operationarray[j] = operationarray[j + 1];
  81. operationarray[j + 1] = oper;
  82. }
  83. }
  84. }
  85. ContextOperationBaseAttribute opernext = operationarray[0];
  86. for (int i = 1; i < operationarray.Length; i++)
  87. {
  88. opernext.NextOperation = operationarray[i];
  89. opernext = operationarray[i];//保持对当前循环对象的上级对象的引用。
  90. }
  91. return operationarray[0];
  92. }
  93. public MethodInfo GetMethodInfo(string methodname)
  94. {
  95. return this.GetType().GetMethod(methodname);
  96. }
  97. public override Guid InitPrimaryKey
  98. {
  99. get
  100. {
  101. return _contextRunTime.InitPrimaryKey;
  102. }
  103. }
  104. public override DateTime InitTime
  105. {
  106. get
  107. {
  108. return _contextRunTime.InitTime;
  109. }
  110. }
  111. public override object GetValue(object key)
  112. {
  113. return _contextRunTime.GetValue(key);
  114. }
  115. public override void SetValue(object key, object value)
  116. {
  117. _contextRunTime.SetValue(key, value);
  118. }
  119. }
  120. }

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

对_JoinOperation方法的解释我们留在后面,这里是一个数据结构。将ContextOperationBaseAttribute
类型串成链表,让方法的执行穿过所有的ContextOperationBaseAttribute处理类。

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

为了让绑定对象支持上下文后期绑定,需要一个特性作为表示。
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. namespace ContextModule
  9. {
  10. /// <summary>
  11. /// 确定设置类是否需要后期动态绑定到上下文。
  12. /// 使用该特性的类将是上下文活跃的,只有在使用的时候才确定当前上下文。
  13. /// </summary>
  14. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
  15. public class ContextEveningBoundAttribute : Attribute
  16. {
  17. public ContextEveningBoundAttribute() { }
  18. private bool _isEvening;
  19. /// <summary>
  20. /// 指定对象是否需要后期动态绑定上下文。
  21. /// </summary>
  22. public bool IsEvening { set { _isEvening = value; } get { return _isEvening; } }
  23. }
  24. }

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

  1. public ContextModuleBaseObject()
  2. {
  3. if (typeof(T).GetCustomAttributes(typeof(ContextEveningBoundAttribute), false) != null)
  4. {
  5. _IsEvening = true;
  6. return;
  7. }
  8. //前期静态绑定上下文
  9. if (ContextRuntime.CurrentContextRuntime == null)
  10. throw new Exception("上下文环境未能初始化,请检查您的代码入口是否启用了ContextRuntime对象。");
  11. _contextRunTime = ContextRuntime.CurrentContextRuntime;
  12. _InitContextHandler<T>();
  13. }

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

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

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

AOP全称为“面向切面编程”。对象在运行时具备多个面,其实在.NET里面我们习惯性的用特性(Attribute)来表达这个概念。因为不需要改动任何代码就可以将特性加到对象中的任何元素中去,在不同的业务环节或者说是功能环节就能动态的转动元素体现出“切面”的优势,当然具体的实现可能很多种,这里使用特性来完成。
在此约定任何处理对象方法的“面”都将被抽象。这里我将命名为ContextOperationBaseAttribute该特性表示所有附加到方法上的特性的基类,对“面”的抽象。
那么不同类型的面将有着不同的操作行为,比如:记录日志的特性、计算性能的特性、认证安全的特性他们都有着不同的行为和属性,所以这里我们还需要提取一个顶层接口作为行为类的特性处理抽象。这里我将命名为IContextOperationHandler该接口作为统一执行行为特性的高层依赖。其实这里也体现出依赖倒置原则,依赖抽象不依赖具体实现。
完整的ContextOperationBaseAttribute类:
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. namespace ContextModule
  9. {
  10. /// <summary>
  11. /// 上下文操作动作特性化基类。
  12. /// 所有对上下文中的类、方法的过滤操作都必须继承此类。
  13. /// </summary>
  14. public abstract class ContextOperationBaseAttribute : Attribute, IContextOperationHandler
  15. {
  16. /// <summary>
  17. /// 过滤器的处理顺序,从小到大的方式进行处理。
  18. /// </summary>
  19. public int OperationSort { getset; }
  20. /// <summary>
  21. /// 下一次过滤操作类
  22. /// </summary>
  23. internal ContextOperationBaseAttribute NextOperation { getset; }
  24. /// <summary>
  25. /// 开始处理过滤对象
  26. /// </summary>
  27. /// <typeparam name="Result">方法的返回值类型</typeparam>
  28. /// <param name="actionmethod">调用的方法包装</param>
  29. /// <param name="paramarray">方法的有序参数</param>
  30. /// <returns></returns>
  31. public virtual Result ResultAction<Result>(ContextMethodInfo actionmethod, params object[] paramarray)
  32. {
  33. object result = null;
  34. if (!actionmethod.IsPost)
  35. {
  36. result = (this as IContextOperationHandler).Operation(actionmethod, paramarray);
  37. if (this.NextOperation != null)
  38. return this.NextOperation.ResultAction<Result>(actionmethod, paramarray);
  39. }
  40. if (result != null)
  41. return (Result)result;
  42. return default(Result);
  43. }
  44. public abstract object Operation(ContextMethodInfo contextmethod, params object[] paramarray);
  45. }
  46. }
作为抽象的顶层类需要完成派生类重复的劳动。这里实现了一个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处理特性的对应关系。
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. namespace ContextModule
  9. {
  10. /// <summary>
  11. /// 特定于上下文的过滤器映射表。
  12. /// 上下文中的任何方法如果需要进行上下文管理的,则使用ContextModule.ContextOperationBaseAttribute特性派生类进行管理。
  13. /// 所有附加于方法、类上的特性管理类都将被映射到ContextModule.ContextFilterHandlerMap实例中。
  14. /// </summary>
  15. public class ContextFilterHandlerMap : Dictionary<string, ContextOperationBaseAttribute>
  16. {
  17. public ContextFilterHandlerMap() { }
  18. /// <summary>
  19. /// 获取方法对应的过滤器处理特性
  20. /// </summary>
  21. /// <param name="mapname">映射Key</param>
  22. /// <returns>ContextOperationBaseAttribute特性实例</returns>
  23. public ContextOperationBaseAttribute MapOperation(string mapname)
  24. {
  25. return this[mapname];
  26. }
  27. /// <summary>
  28. /// 设置过滤器与特定方法的映射
  29. /// </summary>
  30. /// <param name="mapname">映射Key</param>
  31. /// <param name="operationlist">过滤器特性基类ContextOperationBaseAttribute</param>
  32. public void MapOperation(string mapname, ContextOperationBaseAttribute operationlist)
  33. {
  34. this.Add(mapname, operationlist);
  35. }
  36. }
  37. }

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

  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.IO;
  9. namespace ContextModule
  10. {
  11. /// <summary>
  12. /// 上下文操作管理接口
  13. /// </summary>
  14. public interface IContextOperationHandler
  15. {
  16. /// <summary>
  17. /// 开始上下文处理
  18. /// </summary>
  19. /// <param name="contextmethod">CRL目前正在执行的上下文方法的信息。
  20. /// 可以通过ContextMethodInfo实例获取方法详细信息。</param>
  21. ///<param name="paramarray">参数数组</param>
  22. object Operation(ContextMethodInfo contextmethod, params object[] paramarray);
  23. }
  24. }

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

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

2.5.动态入口的实现

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

1):委托实现入口

通过使用System.Delegate动态派生类型来完成对方法的调用,但是委托对于方法的签名必须是强类型的,所以很难做到通用的调用入口。
2):反射实现入口(通过扩展方法在OBJECT基类中加入获取MethodInfo对象的方法,使用时通过该方法获取调用方法的信息)
通过扩展方法在System.Object中加入一个扩展方法用来获取调用方法的信息,然后通过反射动态的调用,这种方法只比较常用的。但是如何框架是在.NET2.0中使用的扩展方法还不能实现,这里我是在ContextModuleBaseObject基类中加了一个类似扩展方法的方式。绑定对象可以很方便的获取到调用方法的MethodInfo对象。
3):完美的动态编译(向抽象、多态敬礼)
最为完美的是扩展代码生成提供程序,在使用的对象里面在派生一个类,专门用来进行多态的转移,让高层的调用顺利进入到派生的类中。不过比较复杂。
这里是使用第二种方式使用的:
  1. /***
  2.  * author:深度训练
  3.  * blog:http://wangqingpei557.blog.51cto.com/
  4.  * **/
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.Reflection;
  9. namespace ContextModule
  10. {
  11. /// <summary>
  12. /// 面向上下文的操作类。
  13. /// 对上下文发起的方法调用需要通过该基类进行调用才能让我们的扩展点使用成为可能。
  14. /// </summary>
  15. public static class ContextAction
  16. {
  17. /// <summary>
  18. /// 在面向上下文的环境中进行方法的调用。
  19. /// </summary>
  20. /// <typeparam name="PostObjectType">调用的上下文绑定对象类型</typeparam>
  21. /// <typeparam name="ResultType">方法的返回类型</typeparam>
  22. /// <param name="post">调用的上下文绑定对象的实例</param>
  23. /// <param name="method">方法的信息对象MethodInfo,通过Oject.GetContextMethodInfo方法自动获取。</param>
  24. /// <param name="paramarray">方法的有序参数集合</param>
  25. /// <returns>ResultType泛型类型指定的返回实例</returns>
  26. public static ResultType PostMethod<PostObjectType, ResultType>(PostObjectType post, MethodInfo method, params object[] paramarray)
  27. where PostObjectType : ContextModuleBaseObject<PostObjectType>
  28. {
  29. _LockPostObejctIsEveningBound<PostObjectType>(post);
  30. string key = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name);
  31. if (!ContextRuntime.CurrentContextRuntime.FilterMap.ContainsKey(key))
  32. {
  33. throw new Exception(string.Format("方法{0}未经过上下文进行管理。", key));
  34. }
  35. ContextMethodInfo contextmethod = new ContextMethodInfo(method, post);
  36. return ContextRuntime.CurrentContextRuntime.FilterMap[key].ResultAction<ResultType>(contextmethod, paramarray);
  37. }
  38. /// <summary>
  39. /// 检查调用实例类是否属于后期绑定。
  40. /// 通过使用ContextModule.ContextEveningBound(IsEvening = true)方式指定后期绑定上下文。
  41. /// </summary>
  42. private static void _LockPostObejctIsEveningBound<PostObjectType>(PostObjectType post)
  43. where PostObjectType : ContextModuleBaseObject<PostObjectType>
  44. {
  45. ContextModuleBaseObject<PostObjectType> contextclass = post as ContextModuleBaseObject<PostObjectType>;
  46. if (contextclass._IsEvening)
  47. contextclass._EveningBoundChildClass<PostObjectType>();
  48. }
  49. }
  50. }

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

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

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

4.面向上下文的领域模型(DMM)
基于上下文的使用模式可以进行领域模型的初步构造,可以先向领域中的大比例结构靠近,将业务模型逻辑归纳到一定的Context中,对业务的模块化梳理也是一种实现。
在分层架构中的业务逻辑层可能需要加入上下文的管理,将业务模型进行运行时控制。比如订单处理,将订单业务流程相关的模型对象归纳为一块。比如用户相关,将用户管理的业务流程相关的模型对象归纳为一块,确实是很有意思。

本文出自 “专注C#、.NET” 博客,请务必保留此出处http://wangqingpei557.blog.51cto.com/1009349/958452

相关文章:

英语之弱元音Schwa

IELTS Speaking - 学会英语Schwa弱元音&#xff0c;你的口语也将充满英伦味 https://baijiahao.baidu.com/s?id1596905156544848616&wfrspider&forpc http://blog.sina.com.cn/s/blog_95e5f8a601017jr4.html https://www.guokr.com/blog/440820/ 打傻方进 百家号04-05…

工厂方法模式和抽象工厂模式

工厂方法模式和抽象工厂模式工厂方法模式抽象工厂模式总结:工厂方法模式 #include <string> #include <iostream>// Abstract class Splitter { private:/* data */ public:Splitter(/* args */);virtual ~Splitter(); public:virtual void split() 0; };Splitte…

关于JQuery中的ajax请求或者post请求的回调方法中的操作执行或者变量修改没反映的问题...

前段时间做一个项目&#xff0c;而项目中所有的请求都要用jquery 中的ajax请求或者post请求&#xff0c;但是开始处理一些简单操作还好&#xff0c;但是自己写了一些验证就出现问题了&#xff0c;比如表单提交的时候&#xff0c;要验证帐号的唯一性&#xff0c;所以要在submit前…

下列关于Java多线程并发控制_下列关于Java多线程并发控制机制的叙述中,错误的是...

下列叙述成都望江楼的造景手法有()。竹文化景观应体现科学性与艺术性的和谐统一&#xff0c;关于既要满足植物的生态习性&#xff0c;又能体现美学价值。在中国传统的审美趣味、多线伦理道德上&#xff0c;竹在造园中被拟人化为( )的代表。程并错误下列不属于竹文化旅游的发展趋…

一.vtun源码学习笔记

1.守护进程 &#xff08;1&#xff09;守护进程简介 守护进程,也就是我们通常所说的Daemon进程。它是一个生存期较长的进程&#xff0c;它通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。 它与普通进程相比有下面这些特殊性&#xff1a;守护进程最重要的…

获取data 数据

export function getData(el, name, val) {const prefix data-if (val) {return el.setAttribute(prefix name, val)}return el.getAttribute(prefix name) } 转载于:https://www.cnblogs.com/hss-blog/p/9781816.html

java jwks_Java SpringBoot 如何使用 IdentityServer4 作为验证服务器学习笔记

这边记录下如何使用IdentityServer4 作为 Java SpringBoot 的 认证服务器和令牌颁发服务器。本人也是新手&#xff0c;所以理解不足的地方请多多指教。另外由于真的很久没有写中文了&#xff0c;用词不太恰当的地方也欢迎新手大佬小伙伴指出&#xff0c;一起进步。另外这边令牌…

git 快速复制一个新项目

gitlab创建项目a及一个主分支master 本地整体复制已有项目b 本地克隆gitlab上新创建的项目a&#xff08;git clone gitlab地址&#xff09; 将本地克隆过来的a文件夹下的.git目录拷贝到b项目下&#xff08;cp -r .git …/b&#xff0c;注意是两个点&#xff0c;不是三个点,写两…

微信公众平台消息接口星标功能

【微信公众平台星标功能接口被撤销】微信公众平台消息接口中的星标功能&#xff0c;被悄悄的去掉了。 原因应该是有的账号在程序中大量使用星标功能&#xff0c;造成微信服务器存储记录过于宠大。 现在要继续使用星标功能&#xff0c;只能在后台手工操作。 <xml> <ToU…

国庆双节长假旅游出行必装的手机软件

长假即将来临&#xff0c;如果打算出去溜达溜达&#xff0c;透透气的朋友&#xff0c;本文可千万别错过了。今天将介绍几款应用是旅途中绝对不能缺少的&#xff0c;这些应用都非常好用、功能全面&#xff0c;是出行必备的手机软件&#xff0c;希望可以伴你度过快乐的十一长假。…

Windbg双机调试环境配置(Windows7/Windows XP+VirtualBox/VMware+WDK7600)

简介&#xff1a;Windbg双机调试内核、驱动 下载软件&#xff1a; 下载Windbg(GRMWDK_EN_7600_1.ISO)下载VirtualBox 5.2/VMware 12一、安装WDK&#xff0c;这里要提一点的是Debugging Tools for Windows一定要打勾&#xff0c;因为我们后面就是要通过这个工具来进行双机调试的…

哈夫曼树的java实现_java实现哈夫曼树

哈夫曼译码,就是将输入的译码还原成对应的字符。 抽象的算法描述:将建立哈夫曼树、实现哈夫曼编码、哈夫曼译码都定义成 子函数的的形式, 然后在主函数中调用它们......数据结构课程设计设计题目: 哈夫曼树及其应用学 院:计算机科学与技术 专业:网络...用哈夫曼树实现图像压缩_…

on-my-zsh git 仓库下运行卡顿

在 oh-my-zsh 进入 包含 git 仓库目录时&#xff0c;执行 ls 时会比较卡顿 原因&#xff1a; oh-my-zsh 要获取 git 更新信息 解决办法&#xff1a; 设置 oh-my-zsh 不读取文件变化信息&#xff08;在 git 项目目录执行下列命令&#xff09; $ git config --add oh-my-zsh…

oracle, group by, having, where

选择列表中如果包含有列、表达式时&#xff0c;这个列、表达式必须包含在Group By子句中。另外&#xff0c;如果采用了表达式的话&#xff0c;则数据库管理员即使在选择列表中采用了别名&#xff0c;但是在Group By子句中仍然必须采用表达式的完整表达方式&#xff0c;而不能够…

[转载] CSS模块化【封装-继承-多态】

第一次听到“CSS模块化”这个词是在WebReBuild的第四届“重构人生”年会上&#xff0c;当时我还想&#xff0c;“哈&#xff0c;CSS也有模块化&#xff0c;我没听错吧&#xff1f;”事实上&#xff0c;我没听错&#xff0c;你也没看错&#xff0c;早就有CSS模块化这个概念了。之…

用jQuery写的一个翻页,并封装为插件,

用jQuery写的一个翻页&#xff0c;并封装为插件&#xff0c; 1 *{2 margin:0;3 padding: 0;4 list-style: none;5 text-decoration: none;6 }7 .page{8 width:500px;9 margin:100px auto; 10 color: #ccc; 11 } 12 .page a{ 13 display: inlin…

Ubuntu 将 /home 或 /var 目录挂载到新的分区

背景 在使用 docker 的过程中&#xff0c;docker 会将某些产物放到 /var/lib/docker/volumes 这会占用很大的跟目录磁盘空间&#xff0c;于是想办法将 /var 目录挂载到另一个一个单独的磁盘上面。 参考链接如下&#xff1a;Ubuntu将var目录挂载到新硬盘 步骤 1. 查看当前磁…

php 500 内部服务器错误,php 500 - 内部服务器错误的解决方法

php 500 - 内部服务器错误的解决方法发布时间&#xff1a;2020-11-04 09:55:31来源&#xff1a;亿速云阅读&#xff1a;71作者&#xff1a;小新小编给大家分享一下php 500 - 内部服务器错误的解决方法&#xff0c;相信大部分人都还不怎么了解&#xff0c;因此分享这篇文章给大家…

网游生命周期在百度指数曲线上呈“M”形分布,各阶段搜索行为呈一定特征

&#xff08;本文转载自&#xff1a;http://data.baidu.com/youxi3/part1.html&#xff09; 产品生命周期就是一种新产品从开始进入市场到被市场淘汰的整个过程&#xff0c;进人和退出市场分别标志着周期的开始和结束。网络游戏的生命周期一般可以划分成测试期、成长期、成熟期…

配置Open***使用User/Pass方式验证登录

Open***和PPTP ***相比存在诸多的优势&#xff0c;最明显的是Open***支持NAT穿越&#xff0c;也就是说在nat环境下使用open***只需要一个在路由器上做一个端口映射即可&#xff01;不需要其他路由的支持&#xff0c;要知道不是所有的路由器都支持配置NAT穿越&#xff0c;只有高…

常惠琢 201771010102《面向对象程序设计(java)》第七周学习总结

实验七 继承附加实验 实验时间 2018-10-11 1、实验目的与要求 &#xff08;1&#xff09;进一步理解4个成员访问权限修饰符的用途&#xff1b; &#xff08;2&#xff09;掌握Object类的常用API用法&#xff1b; &#xff08;3&#xff09;掌握ArrayList类用法与常用API&#…

nginx php站点配置文件,php网站修改默认访问文件的nginx配置

搭建好lnmp后&#xff0c;有时候并不需要直接访问index.php&#xff0c;配置其他的默认访问文件比如index.html这时候需要配置一下nginx才能访问到你想要设置的文件直接上代码&#xff0c;如下是我的配置的一份简单的nginx到php-fpm的站点&#xff0c;该站点默认访问目录/ecmob…

CMake 打包已经存在的动态库生成 target

一. 背景 在 CMakeLists.txt 中&#xff0c;某模块 A 通过 add_subdirectory 引入模块 B &#xff0c;模块 B 通过 add_subdirectory 引入模块 C。模块 C 里面本身就是一个开源的动态库&#xff0c;比如 libtask。目的想要将 C 模块打包成一个 Target &#xff0c;以便在 A 模…

Ruby: Ruby脚本在测试中的使用

如果存在以下的场景&#xff1a;1. 放置在公司的读报机服务器&#xff0c;每天都是开启的&#xff1b;2. 读报机每天下载报纸&#xff0c;一旦成功/失败&#xff0c;就会有相应的记录生成在某个xml文件中&#xff1b;3. 现在有过去一年的读报机下载xml&#xff0c;大约有200多份…

cocos2d-xna for win8源代码轻松移植cocos-xna for wp游戏

无意间看到杨哥弄了一个cocos2d-xna for win8出来可惜没有放出源代码&#xff0c;我试着要了一下结果他没理我&#xff0c;各种画圈圈。 那我只好自己弄一个了&#xff0c;源代码放出大家供交流学习使用&#xff0c;像杨哥说的一样就是一点小bug很容易该成把wp改成win8版的&…

Oct 2018

Tasks motion planning code in Autonomous DrivingUdacity RoboticsNDCoursera RoboticsMotion PlanningEstimationF1/10 racecarProject Overview 1. Mooc Udacity RoboticsND: project 3 Coursera Robotics Motion Planning HomeworkNote --> Post  2. Leetcode ☆☆ …

Springboot+mybatisplus搭建新闻管理系统

模块化实现&#xff0c;一周轻松搭建 前端 后端 项目源码及其教程回复已三连备注邮箱领取

SQL Server存储过程输入参数使用表值

在2008之前如果我们想要将表作为输入参数传递给SQL Server存储过程使比较困难的&#xff0c;可能需要很多的逻辑处理将这些表数据作为字符串或者XML传入。 在2008中提供了表值参数。使用表值参数&#xff0c;可以不必创建临时表或许多参数&#xff0c;即可向 Transact-SQL 语句…

ef core mysql 字符集,EF Core 基础知识

数据库连接字符串在 ASP.NET Core 添加配置片段&#xff1a;{"ConnectionStrings": {"BloggingDatabase": "Server(localdb)\\mssqllocaldb;DatabaseEFGetStarted.ConsoleApp.NewDb;Trusted_ConnectionTrue;"}}然后&#xff0c;配置对应的DbCont…

模板特化,全特化,偏特化,全部特化,部分特化的含义

模板特化&#xff0c;任何针对模板参数进一步进行条件限制设计的特化版本。《泛型思维》 全特化就是全部特化&#xff0c;即针对所有的模板参数进行特化。《c primer》偏特化就是部分特化&#xff0c;即针对部分模板参数进行特化。《c primer》全特化和偏特化的定义不是很严格&…