在过去的半年里,定期或者不定期地写点东西已经成为了我的一种习惯。可是最近两个月来一直忙于工作的事情一直足够的时间留给自己,虽然给自己列了很长一串写作计划,可是心有余而力不足。这一段工作主要是帮助公司开发一套分布式的开发框架,对一些技术和设计方法有了一些新的认识。这两天的工作主要是如何把Enterprise Library V3.1的PIAB(Policy Injection Application Block)引入到我们自己的框架中,为次对PIAB进行了一些研究,借此机会与大家一起分享。

一、Business Logic 和 Infrastructure Logic的分离

对于任何一个企业级应用的开发人员来说,他们编写的代码不仅仅是处理单纯的业务逻辑,同时还需要处理很多的非业务方面的逻辑,比如:Caching、Transaction Enlist、Authorization、Auditing、Exception Handling、Logging、Validation甚至是Performance Counter。我习惯把这些非业务逻辑成为Infrastructure Logic。由于这些Infrastructure Logic通常根据具体的需求注入的具体的业务逻辑的执行中,所以又被更多地成为Crosscutting Concern。

如果按照传统的OO的编程方式,我们通常将这些Business Concern和Crosscutting Concern糅合在一起,从而导致了某种意义的紧耦合。这种紧耦合在一个大型的企业级应用可能是会给应用的可扩展性带来致命的影响。所以必须以某种方式实现Business Concern和Crosscutting Concern的分离,这通常是AOP实现的目标。

举个简单的例子,我们现在有如下一个简单的订单处理操作,可能具体的业务逻辑很简单。但是此操作面临的非业务逻辑可能有很多。比如:

  • 在处理之前进行Authorization,确定当前的用户是否具有此操作的权限。
  • Auditing,记录处理的用户、处理时间等等。
  • Transaction Enlist,将所有Data Access操作纳入同一个Transaction,保证数据的一致性。
  • 进行Exception Handling。
  • 如果出现异常,记录日志。

上面的这些可以体现在线面的代码中:

   1: public void ProcessOrder(Order order)
   2: {
   3:     Authorize();
   4:     Audit();
   5:     using (TransactionScope scope = new TransactionScope())
   6:     {
   7:         try
   8:         {
   9:             OrderDataAccess();
  10:         }
  11:         catch (Exception ex)
  12:         {
  13:             HandleExceptoin(ex);
  14:             Log();
  15:         }
  16:     }
  17: }

可以看到上面这么多行代码中,和业务相关的就一行而已。虽然对这些非业务逻辑的实现通常通过调用一个封装好的方法或者组件完成,你往往只需要Copy Paste就可以了,但是将如此众多的Infrastructure Logic和Business Logic按照这样的方式组合在一起有很多的隐患。

  • 将一些公共的Source code进行大规模的复制不能保证其同一性和正确性。我个人觉得,对于一个程序原来说,如果你频繁地使用到Ctrl + C和Ctrl + V,你应该想到你的代码需要重构。
  • Infrastructure Logic和Business Logic这种耦合性导致对Infrastructure Logic的变动将获导致Source Code的改变。
  • 这样的编程方式增加了程序员的压力,可能对于一个相对Junior的程序员来说,可能根本不知道这个Infrastructure Logic的具体作用何在,而实际上对于最终的程序员来讲,这些应该是对他们透明的。

所以我们需要寻求某种方式将这些Infrastructure Logic注入到某个具体的操作中,而不需要将所有的逻辑实现在具体的方法定义中。比如:你可以通过配置文件进行配置,你也可以通过添加Attribute一声明的方式来实现。而这一切都可以通过Enterprise Library的PIAB来实现。

二、PIAB(Policy Injection Application Block)Overview

PIAB在Enterprise Library 3.0中被引入(注意:不要把PIAB和DIAB混淆,DIAB-Dependence Injection Application Block将在Enterprise Library 4.0中被引入,个人觉得这将又是一个具有重大意义的Application Block,它的使用将会极大的减轻模块或组件的依赖关系,在Software Factory中已经有了广泛的应用)。PIAB提供了一种简单而有效的方式是你能够将你所需的非业务逻辑的Crosscutting concern注入到的某个方法的Invocation stack中。比如可以在方法的调用前注入Auditing的执行,在成功调用后执行Transaction commit的调用,在出现异常时进行Exception Handling的处理。

对于PIAB来说,Policy的意思是:“将对应的处理操作注入到对应的方法调用”。这实际上包含两方面的内容:要注入怎样的处理操作和如何与具体的方法匹配。前者封装在一个称作CallHandler的对象中,而匹配关系通过另一个称作MatchingRule的对象来表现。所以可以说Policy= CallHandler+MatchingRule。

PIAB给我们提供了很多系统自定义CallHandler,在一般情况下它能满足我们绝大部分的需求,比如:

  • AuthorizationCallHandler: Enterprise Library Security Application Block的Authorization来实现。
  • CachingCallHandler:将方法返回值作为Value、参数列表作为Key存入Cache,如果下次出现相同参数列表的调用,则直接将Cache中的值返回,从而免去了再次执行方法对性能的影响。像一般的Cache处理一样,你也可以设置Cache的过期时间。
  • ExceptionCallHandler:通过调用Enterprise Library Exception Handing Application Block实现对一异常处理的支持。
  • LogCallHandler:通过调用Enterprise Library Logging Application Block进行日志的处理。
  • ValidationCallHandler:进行参数的验证等功能,该CallHandler依赖于Enterprise Library Validation Application Block。
  • PerformanceCounterCallHandler。

至于MatchingRule,他实际上是定义了一种如何将CallHander和对应的Method进行匹配的规则。同样的,一系列的MatchingRule被定义出来,比如:基于Assembly的MatchingRule将CallHandler匹配到某个Assembly的所有对象上面;基于Custom Attribute的MatchingRule通过声明在某个元素上的某个Attribute进行匹配。此外还有基于NameSpace、Method Signature、等等的MatchingRule。

如何现有的CallHandler和MatchingRule不能满足你具体的要求,你还可以定义Custom CallHandler或者 Custom MatchingRule。在后续的部分将会介绍一个完整的定义Custom CallHandler的例子。

三、Get Started

经过上面对PIAB的介绍,我想大家对PIAB使用的目标又一个简单的认识,为了加深大家的映像,在本节中,我们做一个简单的Walkthrough,做一个简单的使用PIAB的例子。在这个例子通过PIAB实现两个主要的功能:Caching和Logging。为了简单起见,我们使用一个Console Application来实现这样一个例子。我们假设的一个场景时处理一个订单并将处理后的订单返回。

创建一个Console Application,并添加下面连个Dll Reference

  • Microsoft.Practices.EnterpriseLibrary.PolicyInjection
  • Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers

编写Order Processing的代码:

   1: public class OrderProcessor:MarshalByRefObject
   2: {
   3:     public Order Process(Order order)
   4:     {
   5:         Console.WriteLine("OrderProcessor.Process() is invocated!");
   6:         return order;
   7:     }
   8: }
   9: public class Order { }

至于为什么要继承MarshalByRefObject将会在后面对PIAB的实现原理中介绍。将对OrderProcessor的调用写在Main()。

   1: static void Main(string[] args)
   2: {
   3:     OrderProcessor processor = PolicyInjection.Create<OrderProcessor>();
   4:     Order order = new Order();
   5:     processor.Process(order);
   6:     processor.Process(order);
   7: } 

创建app.config,并通过Enterprise Library Configuration Console进行配置

首先添加一个Logging Application Block

然后 按照相同的方式添加一个Policy Injection Application Block,然后再PIAB节点添加一个Policy。在该Policy下的Match Rules节点下创建一个Method Name Matching Rule (该MatchingRule根据Method name进行CallHandler的匹配),对该MatchingRule进行如下的配置:

接着在Policy下的Handlers节点添加两个CallHandler:Caching Handler和Logging Handler。保持Caching Handler的默认配置,按如下进行Loging Handler的配置:

通过上面的配置,我们将会得到如下的Configuration:

   1: <configuration>
   2:   <configSections>
   3:     <section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   4:     <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   5:   </configSections>
   6:   <policyInjection>
   7:     <policies>
   8:       <add name="Policy">
   9:         <matchingRules>
  10:           <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MemberNameMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  11:             name="Member Name Matching Rule">
  12:             <matches>
  13:               <add match="Process" ignoreCase="false" />
  14:             </matches>
  15:           </add>
  16:         </matchingRules>
  17:         <handlers>
  18:           <add expirationTime="00:05:00" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.CachingCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  19:             name="Caching Handler" />
  20:           <add logBehavior="After" beforeMessage="=================End================="
  21:             afterMessage="================Begin================" eventId="0"
  22:             includeParameterValues="true" includeCallStack="true" includeCallTime="true"
  23:             priority="-1" severity="Information" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  24:             name="Logging Handler">
  25:             <categories>
  26:               <add name="Audit" />
  27:             </categories>
  28:           </add>
  29:         </handlers>
  30:       </add>
  31:     </policies>
  32:   </policyInjection>
  33:   <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
  34:     defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
  35:     <listeners>
  36:       <add source="Enterprise Library Logging" formatter="Text Formatter"
  37:         log="Application" machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  38:         traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  39:         name="Formatted EventLog TraceListener" />
  40:     </listeners>
  41:     <formatters>
  42:       <add template="Timestamp: {timestamp} Message: {message} Category: {category} Priority: {priority} EventId: {eventid} Severity: {severity} Title:{title} Machine: {machine} Application Domain: {appDomain} Process Id: {processId} Process Name: {processName} Win32 Thread Id: {win32ThreadId} Thread Name: {threadName} Extended Properties: {dictionary({key} - {value} )}"
  43:         type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
  44:         name="Text Formatter" />
  45:     </formatters>
  46:     <categorySources>
  47:       <add switchValue="All" name="General">
  48:         <listeners>
  49:           <add name="Formatted EventLog TraceListener" />
  50:         </listeners>
  51:       </add>
  52:     </categorySources>
  53:     <specialSources>
  54:       <allEvents switchValue="All" name="All Events" />
  55:       <notProcessed switchValue="All" name="Unprocessed Category" />
  56:       <errors switchValue="All" name="Logging Errors &amp; Warnings">
  57:         <listeners>
  58:           <add name="Formatted EventLog TraceListener" />
  59:         </listeners>
  60:       </errors>
  61:     </specialSources>
  62:   </loggingConfiguration>
  63: </configuration>

我们现在来运行以下程序,你将会得到如下的输出结果:

从输出的结果可以看出,虽然我们在Main()两次调用了Process方法,但是真正执行的数次只有一次。这是Caching所致,第一次调用PIAB将返回值存入Cache,该Cache Entry的Key为参数Order对象。第二次调用由于传入的参数相同,所有直接将上次的结果返回。

上面我看到了Caching的效果,现在我们在看看Logging。默认情况下,Logging entry被写入Event Log。下图就是我们写入的Log。

EnterLib PIAB系列:

Enterprise Library Policy Injection Application Block 之一: PIAB Overview
Enterprise Library Policy Injection Application Block 之二: PIAB设计和实现原理
Enterprise Library Policy Injection Application Block 之三: PIAB的扩展—创建自定义CallHandler(提供Source Code下载)
Enterprise Library Policy Injection Application Block 之四:如何控制CallHandler的执行顺序

Enterprise Library Policy Injection Application Block 之一: PIAB Overview相关推荐

  1. Policy Injection Application Block

    Microsoft Patterns & Practices团队在2007年发布的Enterprise Library 3.0 February 2007 CTP中,我们惊喜地发现了AOP的踪 ...

  2. 黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block

    代理对象(Proxy Object)会通过Handler链定位到真实对象(Real Object),而Policy则被注入到代理对象和真实对象中.整个流程如图: 我个人对Policy Injectio ...

  3. Enterprise Library: Data Access Application Block配置文件分析篇

    Enterprise Library: Data Access Application Block配置文件分析篇 Enterprise Library提供了Configuration Console配 ...

  4. Enterprise Library1.0 -- DataAccess Application Block

    Enterprise Library 对大家来说应该不陌生,很早我就听说了这个东西,但一直没有时间来学习,最近终于抽出时间来学习Enterprise Library,现在就把我学习过程中的一些实例发上 ...

  5. Enterprise Library 4.1 Application Settings 快速使用图文笔记

    Application Settings 可以使我们运用企业库的配置工具来管理类似于appSetting这样的配置文件中的标记,像我们经常用到的数据库连接字符串. 新建一个Web应用程序 右键点击We ...

  6. Enterprise Library 4.1 Caching Block 图文笔记

    一,下载并安装好Enterprise Library 4.1 二,新建一个Web应用程序 三,右键点击Web.Config 文件 使用 Edit Enterprise Library Configur ...

  7. 微软模式与实践团队发布Enterprise Library 4.1及Unity Application Block 1.2

    微软模式与实践团队发布Enterprise Library 4.1及Unity Application Block 1.2,具体可参看InfoQ的新闻http://www.infoq.com/cn/n ...

  8. Enterprise Library深入解析与灵活应用(3):倘若将Unity、PIAB、Exception Handling引入MVP模式.. .. .....

    最近在做一个Smart Client Software Factory的项目.熟悉SCSF或者CAB的都应该很清楚MVP这种设计模式.MVP是MVC的一种变体,View和Mode分别关注于UI的呈现和 ...

  9. 《Enterprise Library深入解析与灵活应用》博文系列汇总

    Enterprise Library是微软P&P部门开发的众多Open source框架中的一个,最新的版本已经出到了4.1.由于接触Enterprise Library已经有很长的一段时间, ...

最新文章

  1. 表单高级应用和语义化
  2. R语言as.double函数(转化为浮点型)和is.double函数(检验是否为浮点型)实战
  3. JS Math的几个方法使用实例
  4. oracle object_type,Oracle TYPE OBJECT详解 | 学步园
  5. Java CPU占用率高分析
  6. ubuntu9.10 安装 eclipse3.5 的问题
  7. [leetcode]160.相交链表
  8. shell grep 变量_老司机给出的关于 shell 脚本的8个建议,必收!
  9. android 页面无法点击,为什么点击不跳转到下一界面,哪位大神帮瞅瞅
  10. Ubuntu下convert命令将eps转pdf或者tif时出错的问题解决办法
  11. SQL SERVER 大小写敏感设置及排序规则详解
  12. 进退两难的硅谷程序员们
  13. PMBOK第六版学习笔记
  14. 【NLP】HuggingFace BERT 微博评论情感分类
  15. torch.nn.Module.named_buffers(prefix=‘‘, recurse=True)
  16. c语言打砖块游戏说明,c语言打砖块游戏.doc
  17. BD-rate计算原理
  18. Cesium之【高度】量算
  19. (数位dp) 算法竞赛入门到进阶 书本题集
  20. “竞速”智能网联汽车,领头雁为何是长沙?

热门文章

  1. python中大于0的元素全部转化为1,小于0的元素全部转化为0的代码
  2. 调查显示:数据中心行业女性获得报酬比男性多
  3. C++ STL 学习笔记__(6)优先级队列priority_queue基本操作
  4. Android用户界面开发:控件集合
  5. 使用PXE+dhcpd+vsftpd+tftp+kickstart 实现无人值守安装linux
  6. 学习 SCCM 2012 的思路
  7. JavaScript-Load-Image
  8. P1262 间谍网络
  9. docker 源码分析 三(基于1.8.2版本),NewDaemon启动
  10. hdu 3784 继续xxx定律