使用StructureMap扩展ASP.NET MVC三层架构6-Enterprise Library实现异常处理

在上一篇文章的Controller中我们已经看到了,是怎么使用TYExceptionService进行异常的处理与日志记录,本篇文章具体介绍一下TYExceptionService是如使用Enterprise Library 5.0详细的实现。

因为我们的这个系统是面向接口编程的,所以也需要把TYExceptionService注入到Controller,所以也需要一个ITYExceptionService的接口。

ITYExceptionService异常处理服务接口

这个接口中只有一个方法HandleException,接收一个异常对象,一个Dictionary来存非法的属性和一个字符串message自定义的异常信息。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace TYStudioDemo.Interfaces
  8. {
  9.     public interface ITYExceptionService
  10.     {
  11.         string HandleException(Exception exception, Dictionary<string, object> parms, string message);
  12.     }
  13. }

实现类TYExceptionService

详细的说明见代码中的注释

  1. using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Runtime.Serialization;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using TYStudioDemo.Interfaces;
  10. using TYStudioDemo.Commons;
  11.  
  12. namespace TYStudioDemo.Services
  13. {
  14.     public class TYExceptionService : ITYExceptionService
  15.     {
  16.         public string HandleException(Exception exception, Dictionary<string, object> parms, string message)
  17.         {
  18.             //穿件我们自己定义的异常类
  19.             TYException tyException = new TYException(message, exception);
  20.  
  21.             //建立一个StackFrame
  22.             StackFrame frame = new StackFrame(1, true);
  23.  
  24.             //记录抛出异常的文件名,方法名和,行号
  25.             tyException.SetErrorProperty(TYException.CurrentFilename, frame.GetFileName());
  26.             tyException.SetErrorProperty(TYException.CurrentFunction, frame.GetMethod().Name);
  27.             tyException.SetErrorProperty(TYException.CurrentLineNumber, frame.GetFileLineNumber().ToString());
  28.             // parameters, if present
  29.             if (parms != null)
  30.             {
  31.                 foreach (KeyValuePair<string, object> parm in parms)
  32.                 {
  33.                     if (parm.Value != null)
  34.                     {
  35.                         tyException.SetErrorProperty(parm.Key, parm.Value.ToString());
  36.                     }
  37.                 }
  38.             }
  39.  
  40.             //使用EntityPrise Library记录异常信息,TYStudioPolicy配置在Web.Config文件中
  41.             ExceptionPolicy.HandleException(tyException, "TYStudioPolicy");
  42.  
  43.             //返回有好的错误信息到页面
  44.             return tyException.Message;
  45.         }
  46.     }
  47. }

自定义异常类TYException

见代码注释:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace TYStudioDemo.Commons
  9. {
  10.     [Serializable]
  11.     public class TYException : Exception
  12.     {
  13.         //用来记录抛异常的方法,文件名和行号
  14.         public static string CurrentFunction = "Function";
  15.         public static string CurrentFilename = "Filename";
  16.         public static string CurrentLineNumber = "Line Number";
  17.  
  18.         //定义够方法,调用Exception基类的构造方法
  19.         public TYException() : base() { }
  20.         public TYException(string message) : base(message) { }
  21.         public TYException(string message, System.Exception inner) : base(message, inner) { }
  22.         protected TYException(SerializationInfo info, StreamingContext context) : base(info, context) { }
  23.  
  24.         //键值对的构造方法
  25.         //把键值对存入Data,最终通过Enterprise library text foramtter存入数据库中
  26.         public TYException(string message, Exception inner, IDictionary<string, string> props)
  27.             : base(message, inner)
  28.         {
  29.             foreach (KeyValuePair<string, string> entry in props)
  30.                 if (!String.IsNullOrEmpty(entry.Key) && props[entry.Key] != null)
  31.                     Data[entry.Key] = entry.Key;
  32.         }
  33.  
  34.         //获得错误属性值
  35.         public string GetErrorProperty(string key)
  36.         {
  37.             return (Data[key] == null) ? string.Empty : Data[key].ToString();
  38.         }
  39.  
  40.         //添加错误属性值
  41.         public void SetErrorProperty(string key, string value)
  42.         {
  43.             if (!String.IsNullOrEmpty(key) && value != null)
  44.                 Data[key] = value;
  45.         }
  46.     }
  47. }

TYStudioDemo.Commons工程,我移除最开始Utilities工程,用这个工程代替了。这里主要放一些公共的类,或者一些工具类。TYException就放在了这里,当然你有好的地方,完全可以放到其他工程。

mvc-structuremap-commons

Enterprise Library配置Web.Config

具体配置内容是由Enterprise Library工具生成的,首先你需要安装Enterprise Library 5.0,下载地址http://entlib.codeplex.com/ 。安装完成之后找到安装目录下的C:\Program Files (x86)\Microsoft Enterprise Library 5.0\Bin\EntLibConfig.exe,具体的怎么配置规则不做介绍,可以去网上搜索资料。这里给出一个本系统配置成功之后的截图,和相应的Web.Config代码。点击查看大图

enterprise-library-config

Web.Config文件代码:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--
  3.  For more information on how to configure your ASP.NET application, please visit
  4.  
  5. http://go.microsoft.com/fwlink/?LinkId=169433
  6.  
  7.  -->
  8. <configuration>
  9.   <configSections>
  10.     <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
  11.     <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  12.     <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  13.     <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  14.   </configSections>
  15.   <loggingConfiguration name="" tracingEnabled="true" defaultCategory="Category">
  16.     <listeners>
  17.       <add name="Database Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.Database.FormattedDatabaseTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging.Database, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  18.        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Database.Configuration.FormattedDatabaseTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging.Database, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  19.        databaseInstanceName="TYStudioLoggingConnectionString" writeLogStoredProcName="WriteLog"
  20.        addCategoryStoredProcName="AddCategory" formatter="Text Formatter"
  21.        traceOutputOptions="None" />
  22.     </listeners>
  23.     <formatters>
  24.       <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  25.        template="Exception Details:&#xA;{dictionary({key} - {value}{newline})}  &#xA;Message: {message}{newline}&#xA;{newline}&#xA;Timestamp: {timestamp}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}"
  26.        name="Text Formatter" />
  27.     </formatters>
  28.     <categorySources>
  29.       <add switchValue="All" name="Category">
  30.         <listeners>
  31.           <add name="Database Trace Listener" />
  32.         </listeners>
  33.       </add>
  34.     </categorySources>
  35.     <specialSources>
  36.       <allEvents switchValue="All" name="All Events">
  37.         <listeners>
  38.           <add name="Database Trace Listener" />
  39.         </listeners>
  40.       </allEvents>
  41.       <notProcessed switchValue="All" name="Unprocessed Category">
  42.         <listeners>
  43.           <add name="Database Trace Listener" />
  44.         </listeners>
  45.       </notProcessed>
  46.       <errors switchValue="All" name="Logging Errors &amp; Warnings">
  47.         <listeners>
  48.           <add name="Database Trace Listener" />
  49.         </listeners>
  50.       </errors>
  51.     </specialSources>
  52.   </loggingConfiguration>
  53.   <exceptionHandling>
  54.     <exceptionPolicies>
  55.       <add name="TYStudioPolicy">
  56.         <exceptionTypes>
  57.           <add name="All Exceptions" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  58.            postHandlingAction="None">
  59.             <exceptionHandlers>
  60.               <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  61.                logCategory="Category" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
  62.                formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
  63.                priority="0" />
  64.             </exceptionHandlers>
  65.           </add>
  66.         </exceptionTypes>
  67.       </add>
  68.     </exceptionPolicies>
  69.   </exceptionHandling>
  70.   <connectionStrings>
  71.     <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-TYStudioDemo.WebUI-20130331202950;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-TYStudioDemo.WebUI-20130331202950.mdf"
  72.      providerName="System.Data.SqlClient" />
  73.     <add name="TYEntities" connectionString="metadata=res://*/TYEntities.csdl|res://*/TYEntities.ssdl|res://*/TYEntities.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=localhost;initial catalog=NORTHWND;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;"
  74.      providerName="System.Data.EntityClient" />
  75.     <add name="TYStudioLoggingConnectionString" connectionString="Data Source=localhost;Integrated Security=True;Initial Catalog=TYStudio_Logging;Persist Security Info=True;MultipleActiveResultSets=True"
  76.      providerName="System.Data.SqlClient" />
  77.   </connectionStrings>
  78.   <appSettings>
  79.     <add key="webpages:Version" value="2.0.0.0" />
  80.     <add key="webpages:Enabled" value="false" />
  81.     <add key="PreserveLoginUrl" value="true" />
  82.     <add key="ClientValidationEnabled" value="true" />
  83.     <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  84.   </appSettings>
  85.   <system.web>
  86.     <compilation debug="true" targetFramework="4.5" />
  87.     <httpRuntime targetFramework="4.5" />
  88.     <authentication mode="Forms">
  89.       <forms loginUrl="~/Account/Login" timeout="2880" />
  90.     </authentication>
  91.     <pages>
  92.       <namespaces>
  93.         <add namespace="System.Web.Helpers" />
  94.         <add namespace="System.Web.Mvc" />
  95.         <add namespace="System.Web.Mvc.Ajax" />
  96.         <add namespace="System.Web.Mvc.Html" />
  97.         <add namespace="System.Web.Optimization" />
  98.         <add namespace="System.Web.Routing" />
  99.         <add namespace="System.Web.WebPages" />
  100.         <add namespace="TYStudioDemo.DTO" />
  101.       </namespaces>
  102.     </pages>
  103.   </system.web>
  104.   <system.webServer>
  105.     <validation validateIntegratedModeConfiguration="false" />
  106.     <handlers>
  107.       <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
  108.       <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
  109.       <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  110.       <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  111.       <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  112.       <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  113.     </handlers>
  114.   </system.webServer>
  115.   <runtime>
  116.     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  117.       <dependentAssembly>
  118.         <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
  119.         <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
  120.       </dependentAssembly>
  121.       <dependentAssembly>
  122.         <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
  123.         <bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
  124.       </dependentAssembly>
  125.       <dependentAssembly>
  126.         <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
  127.         <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
  128.       </dependentAssembly>
  129.     </assemblyBinding>
  130.   </runtime>
  131.   <entityFramework>
  132.     <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
  133.       <parameters>
  134.         <parameter value="v11.0" />
  135.       </parameters>
  136.     </defaultConnectionFactory>
  137.   </entityFramework>
  138. </configuration>

代码上的全都配置好了,现在该是创建数据库的时候了,在安装Enterprise Library的时候,会提示你安装Source选择安装,安装完成之后,我们可以在里面找到数据库的sql文件。天屹在下面的目录里找到了数据库文件,C:\Users\Administrator\Documents\EntLib50Src\Blocks\Logging\Src\DatabaseTraceListener\Scripts\LoggingDatabase.sql,前面系统documents路径根据自己的机器各不相同,自己修改一下。

不知道什么原因,这个默认的数据库和我们的系统不兼容,两个存储过程有问题AddCategory和WriteLog,这两存储过程的参数和mvc内置的有一些出入,所以需要修改一下,下面列出修改的存储过程sql:

AddCategory

  1. USE [TYStudio_Logging]
  2. GO
  3. /****** Object:  StoredProcedure [dbo].[AddCategory]    Script Date: 04/06/2013 14:46:20 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8.  
  9. ALTER PROCEDURE [dbo].[AddCategory]
  10.     -- Add the parameters for the function here
  11.     @categoryName nvarchar(64),
  12.     @logID int
  13. AS
  14. BEGIN
  15.     SET NOCOUNT ON;
  16.     DECLARE @CatID INT
  17.     SELECT @CatID = CategoryID FROM Category WHERE CategoryName = @categoryName
  18.     IF @CatID IS NULL
  19.     BEGIN
  20.         INSERT INTO Category (CategoryName) VALUES(@categoryName)
  21.         SELECT @CatID = @@IDENTITY
  22.     END
  23.  
  24.     EXEC InsertCategoryLog @CatID, @logID
  25.  
  26.     RETURN @CatID
  27. END

 

WriteLog

  1. USE [TYStudio_Logging]
  2. GO
  3. /****** Object:  StoredProcedure [dbo].[WriteLog]    Script Date: 04/06/2013 14:48:27 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8.  
  9. /****** Object:  Stored Procedure dbo.WriteLog    Script Date: 10/1/2004 3:16:36 PM ******/
  10.  
  11. ALTER PROCEDURE [dbo].[WriteLog]
  12. (
  13.     @eventID int,
  14.     @priority int,
  15.     @severity nvarchar(32),
  16.     @title nvarchar(256),
  17.     @timestamp datetime,
  18.     @machineName nvarchar(32),
  19.     @AppDomainName nvarchar(512),
  20.     @ProcessID nvarchar(256),
  21.     @ProcessName nvarchar(512),
  22.     @ThreadName nvarchar(512),
  23.     @Win32ThreadId nvarchar(128),
  24.     @message nvarchar(1500),
  25.     @formattedmessage ntext,
  26.     @LogId int OUTPUT
  27. )
  28. AS
  29.  
  30.     INSERT INTO [Log] (
  31.         EventID,
  32.         Priority,
  33.         Severity,
  34.         Title,
  35.         [Timestamp],
  36.         MachineName,
  37.         AppDomainName,
  38.         ProcessID,
  39.         ProcessName,
  40.         ThreadName,
  41.         Win32ThreadId,
  42.         Message,
  43.         FormattedMessage
  44.     )
  45.     VALUES (
  46.         @eventID,
  47.         @priority,
  48.         @severity,
  49.         @title,
  50.         @timestamp,
  51.         @MachineName,
  52.         @AppDomainName,
  53.         @ProcessID,
  54.         @ProcessName,
  55.         @ThreadName,
  56.         @Win32ThreadId,
  57.         @message,
  58.         @formattedmessage)
  59.  
  60.     SET @LogId = @@IDENTITY
  61.     RETURN @LogId

如果你先麻烦天屹的下篇文章会对整系统做一个总结,并且提供整个系统成型的源代码下载,系统的Commons工程里面会有Logging数据库的备份,你恢复一下也是可以的。

Logging数据库异常信息:

最后让我们来看一眼异常被记录到Logging数据库中的样子吧,这里面有详细的异常信息,还有我们自定义的异常信息,一目了然,当然Enterprise Library也提供出现异常发送邮件的功能,你只需要到Web.Config添加相应的配置就可以了,这样出现异常会第一时间发送到你指定的邮件。

exceptionmessaeg

到这里异常处日志记录处理就完成了。

下篇文章天屹将提供整个系统的代码下载,敬请期待。



2 评论

  1. cjn   •  

    博主,你好,我也想制作一个自己的博客网站,正在学习asp.net mvc 和js。
    想自己做一个网站,想得到一些指教,谢谢

    • 天屹   •     作者

      我的博客是使用WordPress建立起来的,挺简单的。你可以加我qq,这里回复不方便。416362007

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>