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
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 10 namespace ConsoleApplication1.BLL 11 { 12 [ContextModule.ContextEveningBound(IsEvening = true)] 13 public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order> 14 { 15 DAL.DAL_Order dal_order = new DAL.DAL_Order(); 16 17 [ContextModule.ContextExceptionHandler(OperationSort = 1)] 18 public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel) 19 { 20 return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>( 21 dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel); 22 } 23 [ContextModule.ContextExceptionHandler(OperationSort = 1)] 24 public void SendOrder(Model.Model_Order ordermodel) 25 { 26 ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>( 27 dal_order, dal_order.GetMethodInfo("SendOrder"), ordermodel); 28 } 29 } 30 }
DAL的执行代码:
View Code
1 /*** 2 * author:深度训练 3 * blog:http://wangqingpei557.blog.51cto.com/ 4 * **/ 5 using System; 6 using System.Collections.Generic; 7 using System.Text; 8 9 namespace ConsoleApplication1.DAL 10 { 11 [ContextModule.ContextEveningBound(IsEvening = true)] 12 public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order> 13 { 14 [ContextModule.ContextLogHandler(OperationSort = 1)] 15 [ContextModule.ContextSecurityHanlder(OperationSort = 2)] 16 public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel) 17 { 18 return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now }; 19 } 20 [ContextModule.ContextLogHandler(OperationSort = 1)] 21 public void SendOrder(Model.Model_Order ordermodel) 22 { 23 Console.WriteLine("订单发送成功!"); 24 } 25 } 26 }
上述代码是我模拟一个上下文的执行过程。
3. 上下文共享区域
在每个独立的上下文环境中应该有一片共享的数据存储区域,以备多个上下文对象访问。这种方便性多半存在于项目比较紧张的修改需求的时候或者加新业务的时候扩展方法用的。BLL调用代码:
View Code
1 [ContextModule.ContextExceptionHandler(OperationSort = 1)] 2 public void UpdateOrderSingle() 3 { 4 Model.Model_Order ordermodel = new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now }; 5 //放入上下文共享对象池 6 ContextModule.ContextRuntime.CurrentContextRuntime.SetValue("updateorder", ordermodel); 7 ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>( 8 dal_order, dal_order.GetMethodInfo("UpdateOrderSingle"), null); 9 }
DAL执行代码:
View Code
1 [ContextModule.ContextLogHandler(OperationSort = 1)] 2 public void UpdateOrderSingle() 3 { 4 Model.Model_Order ordermodel = 5 ContextModule.ContextRuntime.CurrentContextRuntime.GetValue("updateorder") as Model.Model_Order; 6 }
4. 上下文运行时环境
对于上下文运行时环境的构建需要考虑到运行时是共享的上下文对象。对于纳入上下文管理的所有对象都需要共享或者说是受控于上下文运行时。
上下文构建:
View Code
1 /*** 2 * author:深度训练 3 * blog:http://wangqingpei557.blog.51cto.com/ 4 * **/ 5 using System; 6 using System.Collections.Generic; 7 using System.Text; 8 9 namespace ContextModule 10 { 11 /// <summary> 12 /// 上下文运行时环境。 13 /// 上下文逻辑运行时环境,环境中的功能都是可以通过附加进来的。 14 /// </summary> 15 public class ContextRuntime : IDisposable 16 { 17 #region IDisposable成员 18 void IDisposable.Dispose() 19 { 20 _currentContextRuntime = null; 21 } 22 #endregion 23 24 protected ContextRuntime() { } 25 private DateTime _initTime = DateTime.Now; 26 /// <summary> 27 /// 获取运行时创建上下文的时间 28 /// </summary> 29 public virtual DateTime InitTime { get { return _initTime; } } 30 private Dictionary<object, object> _runTimeResource = new Dictionary<object, object>(); 31 private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap(); 32 /// <summary> 33 /// 获取上下文中的方法、类过滤器映射表 34 /// </summary> 35 public ContextFilterHandlerMap FilterMap { get { return _filterMap; } } 36 private Guid _initPrimaryKey = Guid.NewGuid(); 37 /// <summary> 38 /// 获取运行时创建上下文的唯一标识 39 /// </summary> 40 public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } } 41 /// <summary> 42 /// 获取上下文共享区域中的数据 43 /// </summary> 44 /// <param name="key">数据Key</param> 45 /// <returns>object数据对象</returns> 46 public virtual object GetValue(object key) 47 { 48 return _runTimeResource[key]; 49 } 50 /// <summary> 51 /// 设置上下文共享区域中的数据 52 /// </summary> 53 /// <param name="key">数据Key</param> 54 /// <param name="value">要设置的数据对象</param> 55 public virtual void SetValue(object key, object value) 56 { 57 _runTimeResource[key] = value; 58 } 59 60 [ThreadStatic] 61 private static ContextRuntime _currentContextRuntime; 62 /// <summary> 63 /// 获取当前上下文运行时对象. 64 /// </summary> 65 public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } } 66 /// <summary> 67 /// 开始运行时上下文 68 /// </summary> 69 /// <returns>ContextRuntime</returns> 70 public static ContextRuntime BeginContextRuntime() 71 { 72 //可以通过配置文件配置上下文运行时环境的参数。这里只是实现简单的模拟。 73 _currentContextRuntime = new ContextRuntime(); 74 return _currentContextRuntime; 75 } 76 } 77 }
对于上下文的入口构建:
View Code
1 using (ContextModule.ContextRuntime.BeginContextRuntime()) 2 { 3 4 }
通过Using的方式我们开始上下文生命周期。
5. 上下文活动对象
上下文对象的绑定需要延后,不能在对象的构建时就创建上下文。
使用后期绑定动态的切入到执行的上下文中。
调用代码,上下文入口:
View Code
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.Data; 9 using ConsoleApplication1.BLL; 10 using ConsoleApplication1.Model; 11 12 namespace ConsoleApplication1 13 { 14 public class Program 15 { 16 public static void Main(string[] args) 17 { 18 BLL.BLL_Order order = new BLL.BLL_Order(); 19 using (ContextModule.ContextRuntime.BeginContextRuntime()) 20 { 21 Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now }; 22 Model.Model_Order resultmodel = ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>(order, order.GetMethodInfo("InsertOrderSingle"), ordermodel); 23 ContextModule.ContextAction.PostMethod<BLL.BLL_Order, object>(order, order.GetMethodInfo("SendOrder"), ordermodel); 24 } 25 26 } 27 } 28 }
6. 上下文在分层架构中的运用
有了上下文的核心原型之后我们可以扩展到分层架构中来,对于分层架构的使用其实很有必要,一般的大型业务系统都是混合的使用模式,可能有C/S、B/S、Mobile终端等等。
对于加入Service层之后BLL、DAL将位于服务之后,对于来自客户端的调用需要经过一些列的身份验证及权限授予。有了WCF之后面向SOA的架构开发变的相对容易点,对安全、性能、负载等等都很完美,所以大部分的情况下我们很少需要控制BLL、DAL的执行运行。
那么没有使用WCF构建分布式的系统时或者是没有分布式的需求就是直接的调用,如WEB的一般开发,从UI到BLL到DAL。或者是普通的Winfrom的项目、控制台项目属于内网的使用,可能就需要控制到代码的执行。
下面我通过演示一个具体的实例来看看到底效果如何。
我以控制台的程序作为演示项目类型,也使用简单的三层架构。
这个再简单不过了吧,为了演示越简单越好,关键是突出重点。
需求:
在DAL对象里面加入一个插入Order实体对象的方法:
View Code
1 /*** 2 * author:深度训练 3 * blog:http://wangqingpei557.blog.51cto.com/ 4 * **/ 5 using System; 6 using System.Collections.Generic; 7 using System.Text; 8 9 namespace ConsoleApplication1.DAL 10 { 11 [ContextModule.ContextEveningBound(IsEvening = true)] 12 public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order> 13 { 14 [ContextModule.ContextLogHandler(OperationSort = 1)] 15 [ContextModule.ContextSecurityHanlder(OperationSort = 2)] 16 public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel) 17 { 18 return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now }; 19 } 20 } 21 }
在这个类的上面有一个特性ContextEveningBound,该是用来表示当前对象属于后期绑定到上下文的对象。同时该类也继承自一个ContextModuleBaseObject<DAL_Order>泛型类,主要作用是将对象强制的绑定到上下文进行管理。
在方法InsertOrderSingle上面有两个特性,ContextLogHandler是用来记录方法的执行日志,ContextSecurityHanlder是用来在方法执行的过程中强制要求管理员认证。
BLL对象代码:
View Code
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 10 namespace ConsoleApplication1.BLL 11 { 12 [ContextModule.ContextEveningBound(IsEvening = true)] 13 public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order> 14 { 15 DAL.DAL_Order dal_order = new DAL.DAL_Order(); 16 17 [ContextModule.ContextExceptionHandler(OperationSort = 1)] 18 public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel) 19 { 20 return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>( 21 dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel); 22 } 23 } 24 }
在BLL对象里面有一个调用DAL对象方法的实例对象,为了演示简单这里没有加入层的依赖注入设计方案,通过直接调用方式。在BLL方法体中有一个专门用来在上下文中调用方法的接口,这是约束目的是为了能让框架切入到方法的执行之前先执行。具体的设计原理我将在下一篇文章中详细讲解。
在方法的上面有一个ContextExceptionHandler特性,目的是安全的调用DAL对象的方法,在有异常的情况下能通过上下文的方式人性化的提示错误信息。这样我们就不需要频繁的编写捕获异常的代码,看起来也不爽,我们要的是代码的整洁、美丽。
UI调用:
View Code
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.Data; 9 using ConsoleApplication1.BLL; 10 using ConsoleApplication1.Model; 11 12 namespace ConsoleApplication1 13 { 14 public class Program 15 { 16 public static void Main(string[] args) 17 { 18 BLL.BLL_Order order = new BLL.BLL_Order(); 19 //开启上下文 20 using (ContextModule.ContextRuntime.BeginContextRuntime()) 21 { 22 Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now }; 23 24 Model.Model_Order resultmodel = 25 ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>( 26 order, order.GetMethodInfo("InsertOrderSingle"), ordermodel); 27 } 28 29 } 30 } 31 }
执行效果:
会先执行日志的记录,然后要求我们输入用户凭证才能继续执行下面的方法。
我输入YES才能继续执行插入的方法。我们可以通过很简单的实现上下文的管理接口,对方法进行控制。
总结:该篇文章只是介绍上下文的作用、原理、优势。下篇文章:“.NET 面向上下文架构模式(实现)”将详细的介绍上下文框架如何开发。[王清培版权所有,转载请给出署名]