1. 启动Enterprise Library Configuration配置工具EntLibConfig.exe
如果安装了Enterprise Library(本例中安装的为Enterprise Library 4.0 - May 2008.msi)。那名该工具在路径%Program Files%/Microsoft Enterprise Library 4.0 - May 2008/Bin下。
2. 打开要配置的配置文件
如app.config、web.config。在本例中使用app.config。
3. 新建Exception Handling Application Block
选择应用程序节点(一般显示为配置文件路径)。执行命令New/Exception Handling Application Block。
4. 添加异常处理策略
选择Exception Handling Application Block节点,执行命令New/Exception Policy。
5. 修改异常策略名称
默认情况下,异常策略的名称为Exception Policy,可将其修改为自己需要的名称,此处将其命名为DataAccessExceptionPolicy。
6. 为策略(异常策略)添加异常类型
选择策略节点,执行命令New/Exception Type。在弹出的类型选择器中选择你要处理的异常类型,如果列表中没有列出,可以通过点击Load an Assembly按钮选择任何自定义异常类型。此处,我们选择DbException。
7. 设置PostHandlingAction
PostHandlingAction的值决定了当异常发生时,异常处理链完成后下一步要采取的动作。三个有效值如下(本例中,我们选择ThrowNewException):
l None. 应用程序执块行所有的异常处理器,并返回false。此后,应用程序继续执行。
l NotifyRethrow. 应用程序块执行所有的异常处理器并返回true。此后,应用程序检查返回值,并再次抛出原始异常。
l ThrowNewException. 应用程序块执行所有的异常处理器,抛出最后一个处理器抛出的异常。
8. 添加异常处理器
选择异常节点,执行命令New/[Handler Type],选择你想要的异常处理器类型。
l Custom Handler. 这个异常处理器允许你配置自定义异常处理器。
l Logging Handler. 这个异常处理器允许你格式化异常信息并使用Logging Application Block 进行日志处理。当你选择这个类型的时候,Logging Application Block 将会自动添加到配置文件中。
l Replace Handler. 这个异常处理器将一个异常替换为另一个异常。
l Wrap Handler. 这个异常处理器使用一个异常包装另一个异常。
9. 执行保存命令
1. 新建解决方案
该解决方案包含一个可启动项目和一个类库项目,可启动项目引用类库项目。本例中可启动项目为WPF Application。
2. 在类库工程中新建自定义异常
该异常作为包装原始异常的异常,异常定义如下:
[global::System.Serializable]
public class DataAccessException : Exception
{
public DataAccessException() { }
public DataAccessException(string message) : base(message) { }
public DataAccessException(string message, Exception inner) : base(message, inner) { }
protected DataAccessException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
3. 配置异常处理器为WrapHandler
将异常处理器名称命名为DataAccessExceptionWrapHandler,异常消息设置为:An error occurred when accessing database.。包装的异常类型为:TechStar.Data.DbClient.DataAccessException, TechStar.Data.DbClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null。配置文件为可启动项目的配置文件。
4. 编写测试代码
在类库工程中编写测试测试代码如下:
public class MasterAccess
{
public static void SelectData()
{
using (SqlConnection conn = new SqlConnection(
@"Data Source=./SQLEXPRESS;Initial Catalog=master;Integrated Security=True"))
{
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from ddd", conn);
command.ExecuteNonQuery();
}
catch(SqlException exception)
{
Exception expToThrow;
ExceptionPolicy.HandleException(exception, "DataAccessExceptionPolicy", out expToThrow);
throw expToThrow;
}
}
}
}
5. 在启动项目中调用该方法
可以看见expToThrow对象的类型为DataAccessException,Message属性的值为:An error occurred when accessing database.。
6. 异常信息的本地化处理
选择异常处理器节点,填写ExceptionMessageResourceName为DataAccessExceptionMessage(可以按自己的需要定义名称,但是需要与资源文件中的资源key一致)。将ExceptionMessageResourceType设置为TechStar.Data.DbClient.DataAccessException, TechStar.Data.DbClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null,在这里,我设置为异常的类型,表示该本地化字符串资源在该类型所在的程序集中。也可以设置为其他类型,但是必须保证设置的类型所在的程序集中有所需的字符串资源。此后,在所选类型所在的程序集中添加一个资源文件,例如Resources.resx,并添加一个字符串资源,本例中该字符串资源的key值为:DataAccessExceptionMessage,Value定义为:访问数据库时出错,包装异常。
1. 使用1.2中建立的解决方案
2. 修改配置文件
删除WrapHandler配置,添加ReplaceHandler配置,并修改相关信息,修改完成后的配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" />
<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" />
</configSections>
<exceptionHandling>
<exceptionPolicies>
<add name="DataAccessExceptionPolicy">
<exceptionTypes>
<add type="System.Data.Common.DbException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException" name="DbException">
<exceptionHandlers>
<add exceptionMessage="An error ocurred when accessing database, this original exception is replaced."
exceptionMessageResourceName="DataAccessReplaceExceptionMessage"
exceptionMessageResourceType="TechStar.Data.DbClient.Properties.Resources, TechStar.Data.DbClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
replaceExceptionType="TechStar.Data.DbClient.DataAccessException, TechStar.Data.DbClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null"
name="DataAccessExceptionReplaceHandler" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
</configuration>
可以看见,有一个replaceExceptionType的属性,用法与上例中的wrapExceptionType属性相同。其他配置与上例基本相同,包括本地化处理。
3. 编写测试代码
在类库工程中编写测试测试代码如下:
public static void SelectDataForReplaceHandler()
{
using (SqlConnection conn = new SqlConnection(
@"Data Source=./SQLEXPRESS;Initial Catalog=master;Integrated Security=True"))
{
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from ddd", conn);
command.ExecuteNonQuery();
}
catch (SqlException exception)
{
Exception expToThrow;
ExceptionPolicy.HandleException(exception, "DataAccessExceptionPolicy", out expToThrow);
throw expToThrow;
}
}
}
4. 在资源文件中添加字符串资源
Key= DataAccessReplaceExceptionMessage, Value=访问数据库时出错,替换异常。
5. 在启动项目中调用该方法
可以看见expToThrow对象的类型为DataAccessException,Message属性的值为:访问数据库时出错,替换异常。
4. 创建日志处理器
在配置管理器中选择异常节点,执行命令New/Logging Handler。
5. 配置日志处理器
可以为新建的日志处理器配置如下属性:
l Name
Value=LoggingHandler
日志处理器的名称,可以设定为符合程序需求的名称
l EventId
Value=100
事件ID,为当前异常类型指定的ID,方便在日志文件中查找该类异常。
l FormaterType
Value=Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.XmlExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
异常格式化类的类型,此处我选择了将异常格式化为Xml文本。还可以选择将异常格式化为纯文本或者使用自己的自定义格式化类型。
l LogCategory
Value=ExceptionCatalog
日志目录,设置对异常信息进行日志时的日志分类。该值在日志模块中配置。
l Priority
Value=10
可以是任何整数值,方便对异常日志进行分类,查找需要优先处理的信息。
l Severity
Value=Stop
该值为一系列枚举值,方便对异常的严重性进行定义,并做相应处理。
l Title
Value=Enterprise Library Logging Exception Handling
日志信息的标题,方便查询用。
l UseDefaultLogger
Value=False
是否使用默认的日志分类,当LogCategory的值在日志配置中无法找到时,是否使用默认的日志分类。
6. 完成配置
配置完成后,配置文件将会exceptionHandlers元素添加如下配置信息
<add logCategory="ExceptionCatalog"
eventId="100"
severity="Stop"
title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.XmlExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null"
priority="0"
useDefaultLogger="false" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null"
name="LoggingHandler" />
7. 测试代码
public static void SelectDataForLogHandler()
{
using (SqlConnection conn = new SqlConnection(
@"Data Source=./SQLEXPRESS;Initial Catalog=master;Integrated Security=True"))
{
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from ddd", conn);
command.ExecuteNonQuery();
}
catch (SqlException exception)
{
bool rethrow = ExceptionPolicy.HandleException(exception, "DataAccessExceptionPolicy");
if (rethrow)
{
throw;
}
}
}
}
以上代码需要将PostHandlingAction设置为NotifyRethrow。如果将设置为ThrowNewException,那么catch块的代码应该按照如下编写:
ExceptionPolicy.HandleException(exception, "DataAccessExceptionPolicy");
此时,该方法在记录日志之后自行抛出原始异常。
1. 实现自定义异常处理器
自定义异常处理器的实现有两个约束:首先,该类必须实现IExceptionHandler接口,其次该类必须具有自定义属性[ConfigurationElementType(typeof(CustomHandlerData))],该属性用来在该类和配置数据之间建立关联。具体的实现代码如下:
[ConfigurationElementType(typeof(CustomHandlerData))]
public class DataAccessExceptionHandler : IExceptionHandler
{
public DataAccessExceptionHandler(NameValueCollection ignore)
{
}
#region IExceptionHandler 成员
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
return exception is SqlException ? new DataAccessException("An error occurred when accessing database.") : exception;
}
#endregion
}
2. 配置自定义异常处理器
使用配置工具配置自定义异常处理器非常简单,在此不再赘述。值得一提的是,自定义异常处理器配置可以添加一些名值对集合。这些数据存储在配置文件中,程序运行时可以从自定义异常处理器的构造函数中获得一个NameValueCollection参数对象,该对象包含那些在配置文件中配置的名值对数据。自定义处理器的配置结果如下:
<exceptionHandlers>
<add NeedLog="true" type="TechStar.Data.DbClient.DataAccessExceptionHandler, TechStar.Data.DbClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
name="DataAccessHandler" />
</exceptionHandlers>
使用配置工具配置时,配置工具无法查找自定义处理器类型,所以需要手动添加自定义异常处理器类型,如上所示。
3. 使用自定义处理器,需要自行实现本地化处理
1. 建立异常格式化类
将异常显示给用户的时候,往往需要隐藏敏感信息,并向用户提供有用的信息。这需要建立一个异常格式化类,该格式化类的基类应该是TextExceptionFormatter。该类从抽象类ExceptionFormatter继承,并提供了特定的实现。然而此实现会将异常的所有信息都收集起来,包括堆栈跟踪信息,而这些信息是应该对最终用户隐藏,所以应该在派生自TextExceptionFormatter的自定义格式化类中隐藏这些敏感信息,具体的实现代码如下:
public class UITextExceptionFormatter : TextExceptionFormatter
{
public UITextExceptionFormatter(TextWriter writer, Exception exception)
: base(writer, exception)
{
}
protected override void WriteStackTrace(string stackTrace)
{
}
protected override void WriteExceptionType(Type exceptionType)
{
}
}
当然,可以根据自己的需要,完全定制异常信息。
2. 实现自定义异常处理器
在该处理器中需要将异常消息显示给用户,所以应该将该处理器的定义放在显示层中。具体的实现如下:
[ConfigurationElementType(typeof(CustomHandlerData))]
public class UIMessageExceptionHandler : IExceptionHandler
{
public UIMessageExceptionHandler(NameValueCollection ignore)
{
}
#region IExceptionHandler 成员
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
Exception newExp = new Exception(string.Format("Exception Id : {0} {1} Error Message :{2} ", handlingInstanceId, Environment.NewLine, exception.Message));
StringBuilder strBuilder = new StringBuilder();
using (StringWriter strWriter = new StringWriter(strBuilder))
{
UITextExceptionFormatter formatter = new UITextExceptionFormatter(strWriter, newExp);
formatter.Format();
MessageBox.Show(strBuilder.ToString(), "Unknown Exception occurred in current operation.", MessageBoxButton.OK, MessageBoxImage.Error);
}
return exception;
}
#endregion
}
在处理异常的过程中,我替换了原有异常,原因是原有异常没有一个Guid信息。该Guid信息是Enterprise Library为每个异常所作的标识。如果异常的详细信息已经被记录日志,那名可以通过该Guid查找到该异常的详细信息,该功能可以为系统的技术支持部门提供服务。生产了新的异常对象之后,通过调用异常格式化对象的格式化方法,可以获得适合最终用户查看的异常信息。通过使用一个系统或者自定制的对话框,可以将异常信息显示给用户。并可在对话框中添加自定义功能,让用户选择处理该异常的各种选择,如关闭当前程序,或者继续,或者发送异常信息给系统管理员等。
3. 异常处理器配置
首先应该为该异常处理建立一个新的策略。并将异常类型设置为Exception类型,这样就可以捕获所有异常。由于该处理器处理最终的所有异常,处理完毕之后不再有后续处理,所以应当将PostHandlingAction设置为None。
4. 使用当前自定义异常处理器
最好能够在一个未知捕获所有未处理异常,不同的架构有不同的处理方法。对于WPF应用程序可以在Application对象的DispatcherUnhandledException事件中处理,处理完毕后应将事件参数的Handled属性设置为true,防止异常继续抛出。
未完成