如何在EHAB(EntLib)中定义”细粒度”异常策略?

简介:

为了解决EntLib的EHAB(Exception Handling Application Block)只能在异常类型级别控制异常处理策略的局限,我在很久之前曾经自定义了一个特殊的异常处理器来提供“细粒度”异常策略的定义(《如何解决EnterLib异常处理框架最大的局限》)。我个人觉得具有一定的实用价值,今天特意对其进行了重构,并将其放到了我在CodePlex上新创建的项目EntLib Extensions

目录
一、完全基于类型的异常策略
二、通过FilterableHandler定义细粒度的异常策略
三、基于“异常筛选”的异常策略
四、异常筛选的匹配优先级

一、完全基于类型的异常策略

EnterLib的异常处理策略基本上可以通过这样的的公式来表示:Exception Policy = Exception Type + Exception Handlers + Post Handling Action,它表达的意思是:“对于某种类型的异常,应该采用哪些Exception Handler去处理,而被处理后的异常还需要采用怎样的后续操作(忽略、抛出处理后异常或者抛出原来捕捉的异常)”。

也就是说,抛出类型的异常类型决定了最终采取的处理策略,这在大部分情况下是可以接受的。但是在很多场景中,不同情况下也可以抛出相同类型的异常,我们期望的行为是:尽管异常类型一样,我们也可以根据具体抛出的异常定义不同的异常处理策略。

一个最为典型的场景就是基于数据库的数据存取,如果你采用的SQL Server,抛出的异常永远只有一种:SqlException。如果完全按照EnterLib EHAB的做法,在任何情况下抛出的SqlException对应的处理方式都是一样的。但是抛出SqlException的情况非常多,比如Server连接断开、认证失败、数据库对象不存在、违反一致性约束等等,如果异常处理框架能够根据最终抛出的异常的具体属性,“智能”地应用相应的策略去处理,这才是我们乐于看到的。

二、通过FilterableHandler定义细粒度的异常策略

为了解决这个问题,我创建了一个特殊的Exception Handler,我将它起名为FilterableHandler。说它特别,是因为FilterableHandler并不从事具体的异常处理操作(比如异常封装、替换、日志等),而是为某个具体的异常类型重新定义了异常处理策略。

由于FilterableHandler本质上就是一个Exception Handler,所以它所提供细粒度异常策略完全定义在基于这个Exception Handler的配置中。为了上读者对“细粒度异常控制”在FilterableHandler德支持有个初步的了解,我们可以来大体了解一下FilterableHandler的配置结构。

   1: <filters>
   2:    <add name="filter1" type="filterType, assemblyName" .../>
   3:    <add name="filter2" type="filterType, assemblyName" .../>
   4: </filters>
   5: <filterTable>
   6:    <add name="filterEntry1" filter="filter1">
   7:        <exceptionHandlers>
   8:           <add name="handler1" type="exceptionHandlerType, assemblyName" .../>
   9:           <add name="handler2" type="exceptionHandlerType, assemblyName" .../>
  10:           <add name="handler3" type="exceptionHandlerType, assemblyName" ...>
  11:        </exceptionHandlers>
  12:    </add>
  13:    
  14:    <add name="filterEntry2" filter="filter2">
  15:        <exceptionHandlers>
  16:           <add name="handler4" type="exceptionHandlerType, assemblyName" .../>
  17:           <add name="handler5" type="exceptionHandlerType, assemblyName" .../>
  18:           <add name="handler6" type="exceptionHandlerType, assemblyName" .../>
  19:        </exceptionHandlers>
  20:    </add>
  21: </filterTable>

从上面给出的配置中,我们可以大体可以看出:针对某个异常的异常策略被分为两个分支,每个分支分别通过对应着<filterTable>结点下定义的两个“筛选器体条目”(filterEntry1和filterEntry2)。而这两个筛选器表分别适用配置的“异常筛选器”filter1和filter2来判断处理的异常是否满足当前分支的条件。当捕获的异常满足相应的分支的筛选条件,则通过当前分支定义的异常处理器列表(第一个分支:handler1、handler2和handler3,第二个分支:handler4、handler5和handler6)。

三、基于“异常筛选”的异常策略

实际上FilterableHandler提供的“细粒度”异常策略是通过“异常筛选”机制实现的。由于我们需要根据捕获的异常来决定应该采用那个分支对其进行处理,而用于分支判断通过一个特殊的组件——异常筛选器(ExceptionFilter)来实现。我们为异常筛选器定义了如下一个简单的接口,唯一的方法Match用于判断给定的Exception对象是否满足当前异常筛选器的筛选条件。

   1: public interface IExceptionHandler
   2: {
   3:    bool Match(Exception exception)
   4: }

我们可以通过实现这个接口来自定义我们需要的异常筛选器,比如我们定义了如下一个DomainFilter。该DomainFilter根据Exception对象某个指定的属性值是否和在预先指定的指列表中,进而判断异常是否满足筛选条件。

   1: public class DomainFilter: IExceptionFilter
   2: {
   3:     public string PropertyName{get; set}
   4:     public string Values{get;set;}
   5:  
   6:     public DomainFilter(string propertyName, string values)
   7:     {
   8:         this.PropertyName = propertyName;
   9:         this.Values = values;
  10:     }
  11:     
  12:     public bool Match(Exception exception)
  13:     {
  14:        var property = exception.GetType().GetProperty(this.PropertyName);
  15:        var value    = property.GetValue(exception, null);
  16:        return this.Values.Split(',').Contains(value.ToString());
  17:     }
  18: }

比如说,现在我们对某种类型的异常进行处理,并且该异常类型具有一个Number属性。现在我们需要针对异常的这个属性来进行分支的选择,比如Number=1、2、和3采用hander1、hander2和handler3进行处理,Number=4、5和6则采用hander4、hander5和handler6进行处理。相应的配置结构如下:

   1: <filters>
   2:    <add name="domainFilter1" type="DomainFilter,assemblyName" property="Number" values="1,2,3" />
   3:    <add name="domainFilter2" type="DomainFilter, assemblyName" property="Number" values="4,5,6" />
   4: </filters>
   5: <filterTable>
   6:    <add name="filterEntry1" filter="domainFilter1">
   7:        <exceptionHandlers>
   8:           <add name="handler1" type="exceptionHandlerType, assemblyName" .../>
   9:           <add name="handler2" type="exceptionHandlerType, assemblyName" .../>
  10:           <add name="handler3" type="exceptionHandlerType, assemblyName" ...>
  11:        </exceptionHandlers>
  12:    </add>
  13:    
  14:    <add name="filterEntry2" filter="domainFilter2">
  15:        <exceptionHandlers>
  16:           <add name="handler4" type="exceptionHandlerType, assemblyName" .../>
  17:           <add name="handler5" type="exceptionHandlerType, assemblyName" .../>
  18:           <add name="handler6" type="exceptionHandlerType, assemblyName" .../>
  19:        </exceptionHandlers>
  20:    </add>
  21: </filterTable>

四、异常筛选的匹配优先级

实际上,这个“异常筛选”机制是根据WCF 4.0的新特性——路由服务的“消息筛选”机制来设计。路由服务具有一个“匹配优先级”的特性,我依然将其借用过来。对于根据配置的异常筛选器决定的异常处理分支,在某个情况下可以出现这样的情况:处理的异常同时满足多个分支的筛选条件。为此在定义筛选表中的每一个筛选器条目(ExceptionFilterEntry)中除了指定异常筛选器的配置名称外,还具有一个类型为整形的priority属性表示匹配的级别。数字越大,级别越高,只有匹配的级别最高的分支会被选用。

   1: <filterTable>
   2:   <add name="filterEntry1" filter="filter1" priority="2">
   3:      <exceptionHandlers>...</exceptionHandlers>
   4:   </add>
   5:   <add name="filterEntry2" filter="filter2" priority="1">
   6:      <exceptionHandlers>...</exceptionHandlers>
   7:   </add>
   8:  
   9:   <add name="filterEntry3" filter="filter3" priority="0">
  10:      <exceptionHandlers>...</exceptionHandlers>
  11:   </add>
  12:  
  13: </filterTable>

比如对于上面的配置,如果filterEntry2和filterEntry3都满足匹配条件,那么会选择优先级别最高的filterEntry2(priority="1"),而放弃级别较低的filterEntry1。如果最高级别的匹配分支有多个,则会抛出ConfigurationErrorsException。


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
SQL 数据安全/隐私保护
通用数据级别权限的框架设计与实现(3)-数据列表的权限过滤
查看上篇文章通用数据级别权限的框架设计与实现(2)-数据权限的准备工作,我们开始数据列表的权限过滤. 原理:我们在做过滤列表时,根据用户权限自动注入到相关SQL中,实现相关过滤,如果拥有全部权限,则不生成相关SQL片段 首先我们来分析一下数据列表的SQL 能看到所有数据的SQL SELECT role.
1129 0
|
27天前
|
程序员
项目中的全局异常是如何处理的
项目中的全局异常处理通常包括对预期异常(程序员手动抛出)和运行时异常的管理。项目已提供`BaseException`作为基础异常类,用于手动抛出异常,并通过`GlobalExceptionHandler`进行全局处理。`
30 4
|
8月前
全局统一异常处理
全局统一异常处理
35 0
|
存储 Go 数据安全/隐私保护
第二十五章 CSP Session 管理 - 选择策略时的注意事项
第二十五章 CSP Session 管理 - 选择策略时的注意事项
|
XML 前端开发 安全
【全网最全】JSR303参数校验与全局异常处理(从理论到实践别用if判断参数了)
【全网最全】JSR303参数校验与全局异常处理(从理论到实践别用if判断参数了)
128 0
【全网最全】JSR303参数校验与全局异常处理(从理论到实践别用if判断参数了)
|
XML JSON 前端开发
设计一个全局异常处理器
cicada: 基于 Netty4 实现的快速、轻量级 WEB 框架;没有过多的依赖,核心 jar 包仅 30KB。
|
安全 程序员 编译器
C++的另一种错误处理策略
这篇短文是讨论一个大多数程序员都感兴趣的一个话题:错误处理。错误处理是编程的一个“黑暗面”。它既是应用程序的“现实世界”的关键点,也是一个你想隐藏的复杂业务。
189 0
|
API
对LinqtoExcel的扩展 【数据有限性,逻辑有效性】
接着上文的内容继续讲,上文中我提到了对Excel操作帮助类库LinqToExcel类库的优缺点和使用方法。我也讲到了自己在使用中碰到的问题,我也开发了一个简单的类库解决,下面就讲解一下这个帮助类。
730 0
|
数据安全/隐私保护
通用数据级别权限的框架设计与实现
个人花了不到2天时间,写了一个通用数据级别权限的框架设计与实现。 欢迎提意见及评论。有空请打赏! 通用数据级别权限的框架设计与实现(1)-相关业务场景的分析 通用数据级别权限的框架设计与实现(2)-数据权限的准备工作 通用数据级别权限的框架设计与实现(3)-数据列表的权限过滤 通用数据级别权限的框架设计与实现(4)-单条记录的权限控制 通用数据级别权限的框架设计与实现(5)-总结与延伸思考 个人代码已经完成,如需要请打赏后通知我。
2287 0
|
JSON 监控 数据库
TP5_接口开发之全局异常控制
前言: 说到异常控制,也许很多会比较陌生,我身边很少人会去写抛异常的代码。但是异常用好了是非常的方便大家开发。首先我们来回顾下哪里可以看到异常,首先我们用框架开发的时候,我们的代码出错或者别的东西。
1289 0