作者:郑佐 在平常的开发过程中处理程序异常必不可少,如何获取更多的异常信息以及对异常信息进行统一处理那是一件值得思考的事情。在.NET程序的开发上,微软为我们提供了一个(我认为)比较好的异常处理模块(Exception Management Application Block),它可以在非特定的.NET应用程序中使用。程序模块和文档说明可以到微软网站上下载,msdn文档也有比较详细的说明。 使用这个模块已经好多次了,看了它里面的代码,觉得写得很不错,有些设计很值得参考和拿来使用。因此写了一些见解同大家交流和分享。 一 、组件的布局 整个组件布局如下: (1) 二、主要代码分析 我认为里面最关键的是两个类:ExceptionManagerSectionHandler 和ExceptionManager类。 (1)ExceptionManagerSectionHandler类 它的功能就是负责读取程序配置文件中的自定义配置节的信息,从代码中看到实现了System.Configuration.IConfigurationSectionHandler接口, public class ExceptionManagerSectionHandler : IConfigurationSectionHandler 在实现IConfigurationSectionHandler.Create方法的时候,把配置文件中的配置信息都读取到ExceptionManagementSettings类实例中进行封装。 在了解方法内部的处理之前,先来看一个标准的配置文件的设置信息: <configuration> <configSections> <section name="exceptionManagement" type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler, configSections> <exceptionManagement mode="on"> <publisher assembly="ExceptionManagementQuickStartSamples" type="ExceptionManagementQuickStartSamples.ExceptionPublisher" exclude="*" include="ExceptionManagementQuickStartSamples.LogonException, ExceptionManagementQuickStartSamples; ExceptionManagementQuickStartSamples.CustomAppException, ExceptionManagementQuickStartSamples"/> <publisher assembly="ExceptionManagementQuickStartSamples" type="ExceptionManagementQuickStartSamples.ExceptionXMLPublisher" exclude="*" include="+Microsoft.ApplicationBlocks.ExceptionManagement.BaseApplicationException, Microsoft.ApplicationBlocks.ExceptionManagement" exceptionFormat="xml" fileName="c:\QuickStartSamplesExceptionLog.xml"/> exceptionManagement> configuration> 自定义节的名称为"exceptionManagement",在Type属性中指名了ExceptionManagerSectionHandler类,这样就可以通过ExceptionManagementSettings config = (ExceptionManagementSettings)ConfigurationSettings.GetConfig("exceptionManagement");来完成封装信息的获取,相当好用。 对exceptionManagement元素的分析就在Create()方法中完成。 public object Create(object parent,object configContext,XmlNode section) { //…… ExceptionManagementSettings settings = new ExceptionManagementSettings(); //…… currentAttribute = nodeAttributes.RemoveNamedItem(PUBLISHER_TYPE); if (currentAttribute != null) publisherSettings.TypeName = currentAttribute.Value; //…… } 里面使用XmlNamedNodeMap.RemoveNamedItem(string)方法返回XmlNode,这样不需要处理多余的东西。里面一个比较好的设计就是可以为publisher元素增加自定义属性和值。如上面配置中的fileName="c:\QuickStartSamplesExceptionLog.xml",这个很灵活,可以在处理特定的异常时使用特定的配置信息。 下面是附加信息填充到publisherSettings的AddOtherAttributes中的代码: for (int i = 0; i < nodeAttributes.Count; i++) { publisherSettings.AddOtherAttributes(nodeAttributes.Item(i).Name,nodeAttributes.Item(i).Value); } PublisherSettings类内部是用NameValueCollection对象来保存这些信息。 该类的另外一个方法就是处理publisher元素的exclude属性和include属性。 private TypeFilter LoadTypeFilter(string[] rawFilter){//…} 里面的几种处理情况: * 表示所有异常, + 表示包括该类及其继承类。 其他按正常处理。 备注:上面一点的设计有时候也很用得上,而且很容易理解。 下面是一些配置的辅助: ExceptionManagementMode枚举,作为exceptionManagemen元素的开关; PublisherMode枚举,作为publisher元素的开关; PublisherSettings映射到publisher的整个元素,里面包含了PublisherMode,PublisherFormat,TypeFilter,NameValueCollection(附加节点)。 (2)ExceptionManager类 该类使用一个私有构造函数来防止被实例化。 private ExceptionManager() { } 在讨论ExceptionManager类之前先简单的来看一下DefaultPublisher类,它实现了IExceptionPublisher接口, public sealed class DefaultPublisher : IExceptionPublisher 功能就是发布异常信息到系统日志中去,由IExceptionPublisher.Publish方法来完成具体工作,下一节我们再具体讨论。 回过头来再看ExceptionManager的主要方法, public static void Publish(Exception exception, NameValueCollection additionalInfo) 在该方法体中会先判断在程序配置文件中是否存在模块配置信息,如果没有就间接得调用DefaultPublisher.Publish方法来发布异常信息; if (ConfigurationSettings.GetConfig(EXCEPTIONMANAGEMENT_CONFIG_SECTION) == null) { PublishToDefaultPublisher(exception, additionalInfo); } EXCEPTIONMANAGEMENT_CONFIG_SECTION表示"exceptionManagement"字符串; 如果有就获取信息并封装到ExceptionManagementSettings实例, ExceptionManagementSettings config = (ExceptionManagementSettings)ConfigurationSettings.GetConfig(EXCEPTIONMANAGEMENT_CONFIG_SECTION); 分析config里的信息来选择调用下面的几个私有静态方法进行异常信息的发布 1)PublishToDefaultPublisher 方法 用默认的方式发布,里面是在调用DefaultPublisher.Publish方法,整个方法如下, private static void PublishToDefaultPublisher(Exception exception, NameValueCollection additionalInfo) { DefaultPublisher Publisher = new DefaultPublisher(); Publisher.Publish(exception, additionalInfo, null); } 2)PublishInternalException 方法 用来发布模块内部处理过程中产生的错误,我们从它的方法体中可以看到它把信息写到了系统的”应用程序(Application)”日志中了。值得注意的是该方法加了internal修饰符。 internal static void PublishInternalException(Exception exception, NameValueCollection additionalInfo) { DefaultPublisher Publisher = new DefaultPublisher("Application", resourceManager.GetString("RES_EXCEPTIONMANAGER_INTERNAL_EXCEPTIONS")); Publisher.Publish(exception, additionalInfo, null); } 3)PublishToCustomPublisher方法 用来发布自定义的异常信息,根据publisher.ExceptionFormat枚举值来决定创建实现了IExceptionXmlPublisher或IExceptionPublisher接口的类的实例。 if (publisher.ExceptionFormat == PublisherFormat.Xml) { IExceptionXmlPublisher XMLPublisher = (IExceptionXmlPublisher)Activate(publisher.AssemblyName, publisher.TypeName); XMLPublisher.Publish(SerializeToXml(exception, additionalInfo),publisher.OtherAttributes); } else { IExceptionPublisher Publisher = (IExceptionPublisher)Activate(publisher.AssemblyName, publisher.TypeName); Publisher.Publish(exception, additionalInfo, publisher.OtherAttributes); } 产生实例的方法如下: private static object Activate(string assembly, string typeName) { return AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assembly, typeName); } 另外一个方法就是把异常信息序列化为XmlDocument对象,定义如下: public static XmlDocument SerializeToXml(Exception exception, NameValueCollection additionalInfo){//…} 方法体中就是把信息添加到xml节点或属性中,生成一个XmlDocument文档。 三、自定义接口 接下来就是怎样来由用户实现自定义的异常处理,在图(1)中我们可以看到程序为我们提个了两个接口,IExceptionXmlPublisher和IExceptionPublisher,并且都定义了Publish方法。 具体如下: public interface IExceptionPublisher { void Publish(Exception exception, NameValueCollection additionalInfo, NameValueCollection configSettings); } public interface IExceptionXmlPublisher { void Publish(XmlDocument exceptionInfo, NameValueCollection configSettings); } 没什么东西,就来说明一下上面方法中使用的参数: Exception表示异常类,指的就是程序中产生的异常,介意程序中定义的异常类从BaseApplicationException类产生,这样可以包含下面信息: 1〉 Environment.MachineName 2〉 Thread.CurrentPrincipal.Identity.Name 3〉 WindowsIdentity.GetCurrent().Name 4〉 AppDomain.CurrentDomain.FriendlyName additionalInfo,指异常的附加信息,包含如下, 1〉 Environment.MachineName 2〉 DateTime.Now 3〉 Assembly.GetExecutingAssembly().FullName 4〉 AppDomain.CurrentDomain.FriendlyName 5〉 Thread.CurrentPrincipal.Identity.Name 6〉 WindowsIdentity.GetCurrent().Name exceptionInfo对象是通过ExceptionManager.SerializeToXml方法将Exception对象序列化为XmlDocument得到的。 configSettings,该对象包含的是由配置文件中增加的一些附加信息,比如上面的fileName="c:\QuickStartSamplesExceptionLog.xml",可以有多个。 另外,在接口方法中的实现就可以由自己处理了,扑获的异常通过ExceptionManager.Publish方法发布,如: private void btnLogon_Click(object sender, System.EventArgs e) { try { if ( DoLogon(txtUserName.Text, txtPassword.Text) == true ) MessageBox.Show("Your Logon Was Successful"); else MessageBox.Show("Logon failed. Invalid user name or password"); } catch( LogonException lex) { // publish Exception using ExceptionManager ExceptionManager.Publish( lex ); MessageBox.Show(lex.Message); } } 备注:LogonException类从BaseApplicationException继承。 对于两个接口的实现,例子代码中有比较详细的参考,这里就不写了。 欢迎大家和我交流,zhzuocn(at)163.com ;我的blog,blog.csdn.net/zhzuo Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=90549 |
|