Fork me on GitHub
微软企业库5.0

在前一篇文章:[EntLib]微软企业库5.0 学习之路——第二步、使用VS2010+Data Access模块建立多数据库项目中我们搭建好了项目的整体多数据库环境,实现了项目的多数据库访问,而整个项目中最主要的异常处理却没有进行部署,今天我们就使用企业库中的Exception Handling+Logging模块为项目加上异常处理以及异常日志记录。

(注:关于Exception Handling和Logging模块的相关基本概念可以查看TerryLee的异常处理日志检测这2篇文章)

     首先说一下企业库Logging模块的个人感觉,个人感觉企业库的日志记录太繁琐了,而且要自定义也比较烦,无法通过简单的配置达到我自己的要求,企业库中的日志记录模块在可以记录许多信息如下:

Timestamp: 2010-6-12 3:16:39

Message: HandlingInstanceID: 669fed01-a758-434b-896e-a8e25ebf8c9b
An exception of type 'System.Exception' occurred and was caught.
----------------------------------------------------------------
06/12/2010 11:16:39
Type : System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : Test
Source : EntLibStudy.Helper
Help link :
Data : System.Collections.ListDictionaryInternal
TargetSite : System.String Test()
Stack Trace :    在 EntLibStudy.Helper.BasePage.Test() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 87
   在 EntLibStudy.Helper.BasePage.<Page_Load>b__0() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 81
   在 Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionManagerImpl.Process[TResult](Func`1 action, TResult defaultResult, String policyName) 位置

以下省略N行。。。。。。

这些信息很多都不是我想要的,我想要的仅仅是异常的提示信息,异常发生的时间,以及异常发生的位置,好方便我们第一时间到异常发生的源头进行调试检查(可能企业库的这些异常信息更加有用,但是我个人认为很多时候都会干扰我们),所以我们仅仅需要其中的几条有用的信息就够了,比如Message,Timestamp、Stack Trace和Severity这4个就基本上够用了,所以我做了个处理,就是使用企业库中Logging模块提供的自定义CustomerTraceListener来实现我们需要的功能。

首先建立一个异常日志记录表(SQLite版)

1 CREATE TABLE [ExceptionLog] (
2     [Id] integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3     [Message] nvarchar(1024) NOT NULL,
4     [LogDate] nvarchar(1024) NOT NULL,
5     [ExceptionLevel] nvarchar(32) NOT NULL,
6     [Exception] ntext NOT NULL
7 )

我编写了一个类继承自CustomTraceListener,并重写了记录方法,具体代码如下:

001 using System;
002 using System.Collections.Generic;
003 using System.Data;
004 using System.Data.Common;
005 using System.Diagnostics;
006 using System.Globalization;
007 using System.Linq;
008 using System.Text;
009   
010 using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
011 using Microsoft.Practices.EnterpriseLibrary.Data;
012 using Microsoft.Practices.EnterpriseLibrary.Logging;
013 using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
014 using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
015 using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
016   
017 namespace EntLibStudy.Helper.EntLibExtension.ExceptionExtension
018 {
019     [ConfigurationElementType(typeof(CustomTraceListenerData))]
020     public class ExceptionCustomerListener : CustomTraceListener
021     {
022         string writeLogSQL = String.Empty;
023         Database database;
024         Exception ex;
025   
026         public ExceptionCustomerListener()
027             : base()
028         {
029             database = DBHelper.CreateDataBase();
030         }
031   
032         public override void TraceData(TraceEventCache eventCache, string source,
033             TraceEventType eventType, int id, object data)
034         {
035             if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
036             {
037                 if (data is LogEntry)
038                 {
039                     LogEntry logEntry = data as LogEntry;
040                     ExecuteSQL(logEntry);
041                 }
042                 else if (data is string)
043                 {
044                     Write(data as string);
045                 }
046                 else
047                 {
048                     base.TraceData(eventCache, source, eventType, id, data);
049                 }
050             }
051         }
052   
053         public override void Write(string message)
054         {
055             ExecuteWriteLogSQL(TraceEventType.Information, DateTime.Now, message, database);
056         }
057   
058         public override void WriteLine(string message)
059         {
060             Write(message);
061         }
062   
063         /// <summary>
064         ///执行SQL
065         /// </summary>
066         /// <param name="logEntry">日志对象</param>
067         private void ExecuteSQL(LogEntry logEntry)
068         {
069             using (DbConnection connection = database.CreateConnection())
070             {
071                 try
072                 {
073                     connection.Open();
074                     using (DbTransaction transaction = connection.BeginTransaction())
075                     {
076                         try
077                         {
078                             ExecuteWriteLogSQL(logEntry, database, transaction);
079                             transaction.Commit();
080                         }
081                         catch
082                         {
083                             transaction.Rollback();
084                             throw;
085                         }
086                     }
087                 }
088                 finally
089                 {
090                     connection.Close();
091                 }
092             }
093         }
094   
095         /// <summary>
096         /// 执行写入日志数据库语句
097         /// </summary>
098         /// <param name="severity">异常等级</param>
099         /// <param name="message">消息</param>
100         /// <param name="db">保存日志的数据库实例</param>
101         private void ExecuteWriteLogSQL(TraceEventType severity, DateTime timeStamp, string message, Database db)
102         {
103             writeLogSQL = (string)this.Attributes["writeLogSQL"];
104             DbCommand cmd = db.GetSqlStringCommand(writeLogSQL);
105             string exceptionMessage = Utils.GetBetweenString(message, "Message :", "Source :", 9);
106             string exceptionInfo = Utils.GetBetweenString(message, "Stack Trace :", "Additional Info:", 13);
107             db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage);
108             db.AddInParameter(cmd, "@LogDate", DbType.DateTime, timeStamp);
109             db.AddInParameter(cmd, "@Level", DbType.String, message);
110             db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo);
111             db.ExecuteNonQuery(cmd);
112         }
113   
114         /// <summary>
115         /// 执行写入日志数据库语句
116         /// </summary>
117         /// <param name="logEntry">日志对象</param>
118         /// <param name="db">保存日志的数据库实例</param>
119         /// <param name="transaction">事务对象</param>
120         private void ExecuteWriteLogSQL(LogEntry logEntry, Database db, DbTransaction transaction)
121         {
122             writeLogSQL = (string)this.Attributes["writeLogSQL"];
123             DbCommand cmd = db.GetSqlStringCommand(writeLogSQL); 
124             string exceptionMessage = Utils.GetBetweenString(logEntry.Message, "Message :", "Source :", 9);
125             string exceptionInfo = Utils.GetBetweenString(logEntry.Message, "Stack Trace :", "Additional Info:", 13);
126             db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage);
127             db.AddInParameter(cmd, "@LogDate", DbType.DateTime, logEntry.TimeStamp.ToLocalTime());
128             db.AddInParameter(cmd, "@Level", DbType.String, logEntry.LoggedSeverity);
129             db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo);
130             db.ExecuteNonQuery(cmd, transaction);
131         }
132     }
133 }

其中在类的初始化的时候获取配置文件的默认数据库对象,通过重写TraceData方法来调用ExecuteSQL方法来执行异常日志插入。

在ExecuteWriteLogSQL方法中有句代码:

1 writeLogSQL = (string)this.Attributes["writeLogSQL"];

这个代码就是从配置文件中Listener的Attributes中获取所配置的执行SQL语句(这里不同于Logging模块自带的数据库以存储过程的记录方式,而是使用配置的SQL语句的方式,因为本项目是面向多数据库的,并不是所有的数据库都有存储过程的,比如SQLite),下面看下具体的配置信息:

pic14

配置文件创建主要分为以下2步:

1、在企业库的配置工具添加一个Exception Handle模块,然后添加一个名为Exception Policy的策略,再为这个策略添加异常类型,默认我选择所有异常类型(All Exceptions),Post Handle Action为: NotifyRethow(对不理解Post Handle Action的处理方式的可以看下下面的解释)

PostHandlingAction 决定了在异常处理链完成后将发生什么活动。默认情况下,PostHandlingAction 被设置为 NotifyRethrow 。
None:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 false 给应用程序。应用程序检查此值以继续运行。
NotifyRethrow:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 true 给应用程序。应用程序检查到此值就重新抛出原始异常。
ThrowNewException:应用程序块为此异常执行所有的处理程序,然后在所有处理程序运行后抛出存在的异常。

2、为异常策略创建处理方式,我这边选择Loggin Exception Handler(在创建的同时配置工具会我们自动创建好Logging模块,并自动创建了一个日志分类:General,不过这个日志分类的默认Listener为event log,就是记录到系统的事件中),这时我们再创建一个CustomerTraceListener选择From File->自定义Listener所在DLL。

这边我碰到了一个问题就是添加了CustomerTraceListener,在对话框中我点击From File选择我编写的自定义Listener所在DLL,可惜没任何反应,不知道是不是要在DLL中做什么处理,所以我只能采用老办法:手写配置文件

首先看下Exception Handle模块的配置信息:

01 <exceptionHandling>
02     <exceptionPolicies>
03       <add name="ExceptionPolicy">
04         <exceptionTypes>
05           <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
06             postHandlingAction="NotifyRethrow">
07             <exceptionHandlers>
08               <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"
09                 logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
10                 formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
11                 priority="0" />
12             </exceptionHandlers>
13           </add>
14         </exceptionTypes>
15       </add>
16     </exceptionPolicies>
17   </exceptionHandling>

接下来是日志模块配置,在日志模块下我配置了3个Listener,其中Custom Trace Listener为我自定义的异常日志记录,Event Log Listener(系统日志记录)Rolling Flat File Trace Listener(文本文件记录,按天回滚记录)为在日志分类General无法正常记录日志时的记录下日志分类General为何无法记录,因为异常日志默认保存到数据库中,但是如果数据库中存在问题,或者链接被关闭这时就无法正常记录异常,所以:

01 <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
02     <listeners>
03       <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging"
04         writeLogSQL="insert into ExceptionLog(Message,LogDate,ExceptionLevel,Exception) values(@Message,@LogDate,@Level,@Exception)"
05         type="EntLibStudy.Helper.EntLibExtension.ExceptionExtension.ExceptionCustomerListener, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
06         traceOutputOptions="None" name="Custom Trace Listener" initializeData=""
07         formatter="Text Formatter" />
08       <add name="Event Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
09         listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
10         source="Enterprise Library Logging" formatter="Text Formatter"
11         log="" machineName="." traceOutputOptions="None" />
12       <add name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
13         listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
14         fileName="rolling.log" formatter="Text Formatter" rollInterval="Day" />
15     </listeners>
16     <formatters>
17       <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
18         template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{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}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
19         name="Text Formatter" />
20     </formatters>
21     <categorySources>
22       <add switchValue="All" name="General">
23         <listeners>
24           <add name="Custom Trace Listener" />
25         </listeners>
26       </add>
27     </categorySources>
28     <specialSources>
29       <allEvents switchValue="All" name="All Events" />
30       <notProcessed switchValue="All" name="Unprocessed Category" />
31       <errors switchValue="All" name="Logging Errors &amp; Warnings">
32         <listeners>
33           <add name="Event Log Listener" />
34           <add name="Rolling Flat File Trace Listener" />
35         </listeners>
36       </errors>
37     </specialSources>
38   </loggingConfiguration>

在配置完后我们就可以进行代码编写,在页面里进行异常控制。

在ASP.NET中,异常处理主要有4种,执行顺序为:Page_Error事件>ErrorPage属性>Application_Error事件> <customErrors>,我这边采用Page_Error,由于在本项目中我已经建立了BasePage,所有的页面都继承这个页面,所以我只需在这个页面中编写Page_Error事件:

01 protected void Page_Error(object sender, EventArgs e)
02         {
03             //获取最新的异常信息
04             var ex = Server.GetLastError();
05             //处理异常
06             HandleException(ex, "ExceptionPolicy");
07             //清空异常
08             Server.ClearError();
09         }
10         /// <summary>
11         /// 异常处理方法
12         /// </summary>
13         /// <param name="ex">异常信息</param>
14         /// <param name="policy">异常处理策略</param>
15         protected void HandleException(Exception ex, string policy)
16         {
17             bool rethrow = false;
18             var exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
19             rethrow = exManager.HandleException(ex, policy);
20             if (rethrow)
21             {
22                 this.RedirectPermanent("~/error.aspx");
23             }
24         }

其中exManager.HandleException(ex, policy)为根据策略名处理异常,我这边使用的ExceptionPolicy,这个策略的处理方式为异常日志记录,它会帮我们调用到我们自定义的ExceptionCustomerListener 类,进行异常日志记录。

这样我们就完成了统一捕获系统中发生的异常了,本文也到此结束,欢迎大家指点!

 

--------------------------------文章扩展分割线-----------------------------------------------

当然企业库中的Exception Handle和Logging模块远不止这些,Exception Handle还提供了异常替换(将指定的异常替换成其他的异常信息),异常包装(将一个异常包装到另外一个异常当中)

Logging模块提供了许多的记录方式,如文本,XML,邮件,消息队列等等,所以我们可以根据我们的需求自由的选择。

本文仅仅就我的实际项目需求进行了简单的扩展,所以可能还有许多的不足,大家可根据自己的需求进行研究扩展,如果大家有好的异常记录处理办法可以提出来让我借鉴下。:)

 

PS:我在文中提到了企业库的Logging模块太繁琐,大家可以看下我的使用log4net完成程序异常日志记录(使用SQLite数据库记录和普通文本记录)这篇文章,如果仅仅是要进行系统的异常记录的话log4net是个不错的选择,配置又方便而且也轻便,单若想完整使用企业库的功能的话就还是使用Exception Handle+Logging这个组合了。

注意:

1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。

2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。

3、项目开发环境为:VS2010+SQL2005。

4、管理员帐户:admin

              密码:admin

源代码下载地址:点我下载

在前端时间开发的时候由于需要将异常保存到数据库中,所以就到网上搜了下专门的日志记录工具,一搜果然很多,比如:log4net,NLog,EntLib Logging等等,但是还是log4net名气最大,所以就下载下来试用了一番,果然很方便,其涵盖了所有常用的日志记录方式具体的可以看下表:

AdoNetAppender 将日志记录到数据库中。可以采用SQL和存储过程两种方式。

AnsiColorTerminalAppender 将日志高亮输出到ANSI终端。

AspNetTraceAppender 能用asp.net中Trace的方式查看记录的日志。

BufferingForwardingAppender 在输出到子Appenders之前先缓存日志事件。

ConsoleAppender 将日志输出到应用程序控制台。

EventLogAppender 将日志写到Windows Event Log。

FileAppender 将日志输出到文件。

ForwardingAppender 发送日志事件到子Appenders。

LocalSyslogAppender 将日志写到local syslog service (仅用于UNIX环境下)。

MemoryAppender 将日志存到内存缓冲区。

NetSendAppender 将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。

OutputDebugStringAppender 将日志输出到Debuger,如果程序没有Debuger,就输出到系统Debuger。如果系统Debuger也不可用,将忽略消息。

RemoteSyslogAppender 通过UDP网络协议将日志写到Remote syslog service。

RemotingAppender 通过.NET Remoting将日志写到远程接收端。

RollingFileAppender 将日志以回滚文件的形式写到文件中。

SmtpAppender 将日志写到邮件中。

SmtpPickupDirAppender 将消息以文件的方式放入一个目录中,像IIS SMTP agent这样的SMTP代理就可以阅读或发送它们。

TelnetAppender 客户端通过Telnet来接受日志事件。

TraceAppender 将日志写到.NET trace 系统。

UdpAppender 将日志以无连接UDP数据报的形式送到远程宿主或用UdpClient的形式广播。

怎么样?看了是不是很心动?如果想学习log4net的话可以看下园子里这位兄弟写的文章:如何使用Log4net创建日志及简单扩展,里面详细介绍了如何使用log4net记录日志,本文呢仅仅就对异常出现后如何记录下来进行讨论,同时也是做个记录方便日后写微软企业库 学习之路有所引用。

在日常的项目开发过程中总会碰到各种各样的异常,我们总是希望异常能第1时间捕获,同时能清楚的知道异常信息、异常发生的时间、发生异常的位置,这样我们好立刻追踪到其发生点来具体解决问题,log4net就很好的帮我们解决了这个问题

现在我们就开始对如何进行异常日志的记录做个分析:

首先是具体的config配置文件,主要分为2个日志记录器,一个是LogToSqlite(记录到SQLite数据库),另外一个是LogToFile(记录到相应的文件),如果要更换到其他数据库也是一样的,值需要更改connectionTypeconnectionString就可以了:

001 <?xml version="1.0" encoding="utf-8" ?>
002 <configuration>
003   <configSections>
004     <section name="log4net" type="System.Configuration.IgnoreSectionHandler, log4net" />
005   </configSections>
006   <log4net>
007     <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender,log4net">
008       <!--BufferSize为缓冲区大小-->
009       <bufferSize value="100" />
010   
011       <!--<param name="BufferSize" value="2" />-->
012       <!--引用-->
013       <connectionType value="System.Data.SQLite.SQLiteConnection, System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" />
014       <!--连接字符串-->
015       <connectionString value="data source=F:\\项目开发\\log4netDemo\\WebApplication1\\bin\\log.db3" />
016       <!--插入语句-->
017       <commandText value="insert into [Log2] ([Author],[LogDate],[Log_Level],[Message],[Location],[Exception]) Values(@Author,@Date,@Level,@Message,@Location,@Exception);" />
018       <commandType value="Text"/>
019       <!--操作者,暂时没用到-->
020       <parameter>
021         <parameterName value="@Author" />
022         <dbType value="String" />
023         <layout type="log4net.Layout.PatternLayout">
024           <conversionPattern value="%property{Operator}" />
025         </layout>
026       </parameter>
027       <!--记录时间-->
028       <parameter>
029         <parameterName value="@Date" />
030         <dbType value="DateTime" />
031         <layout type="log4net.Layout.RawTimeStampLayout" />
032       </parameter>
033       <!--日志等级-->
034       <parameter>
035         <parameterName value="@Level" />
036         <dbType value="String" />
037         <layout type="log4net.Layout.PatternLayout">
038           <conversionPattern value="%level" />
039         </layout>
040       </parameter>
041       <!--异常消息-->
042       <parameter>
043         <parameterName value="@Message" />
044         <dbType value="String" />
045         <layout type="log4net.Layout.PatternLayout">
046           <conversionPattern value="%message" />
047         </layout>
048       </parameter>
049       <!--异常位置-->
050       <parameter>
051         <parameterName value="@Location" />
052         <dbType value="String" />
053         <layout type="log4net.Layout.PatternLayout">
054           <conversionPattern value="%location" />
055         </layout>
056       </parameter>
057       <!--错误-->
058       <parameter>
059         <parameterName value="@Exception" />
060         <dbType value="String" />
061         <layout type="log4net.Layout.PatternLayout">
062           <conversionPattern value="%exception" />
063         </layout>
064       </parameter>
065     </appender>
066     <appender name="LogAllToFile" type="log4net.Appender.RollingFileAppender,log4net">
067       <!--输出格式
068                      每种转换符号都以%开始,后面跟着一个格式符号和换符号。
069                      %-数字 :该项的最小长度,小于最小长度的用空格填充
070                      %m(message):输出的日志消息
071                      %n(new line):换行 
072                      %d(datetime):输出当前语句运行的时刻 
073                      %r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数 
074                      %t(thread id):当前语句所在的线程ID 
075                      %p(priority): 日志的当前优先级别,即DEBUG、INFO、WARN…等 
076                      %c(class):当前日志对象的名称,
077                      %L(line ):输出语句所在的行号 
078                      %F(file name):输出语句所在的文件名
079                      %logger 日志名称
080                  -->
081       <param name="File" value="log\"/>
082       <param name="AppendToFile" value="true"/>
083       <param name="MaxSizeRollBackups" value="100"/>
084       <param name="MaximumFileSize" value="1KB"/>
085       <param name="StaticLogFileName" value="false"/>
086       <param name="DatePattern" value="yyyyMMdd&quot;.log&quot;"/>
087       <param name="RollingStyle" value="Date"/>
088       <layout type="log4net.Layout.PatternLayout">
089         <param name="ConversionPattern" value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger 操作者ID:%property{Operator} 操作类型:%property{ActionType}%n当前机器名:%property%n当前机器名及登录用户:%username %n记录位置:%location%n消息描述:%property{Message}%n异常:%exception%n消息:%message%newline%n%n" />
090       </layout>
091     </appender>
092     <logger name="LogToSqlite">
093       <level value="ERROR"/>
094       <appender-ref ref="ADONetAppender"/>
095     </logger>
096     <!--<logger name="LogToFile">
097       <level value="ALL"/>
098       <appender-ref ref="LogAllToFile"/>
099     </logger>-->
100     <!--所有logger的基础,root的设置在所有logger中都起作用。 
101         当在root和logger中重复设定相同的appender时,你会发现同一日志信息将被记录两次。-->
102     <!--<root>
103       <level value="ERROR"/>
104       ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF
105       <appender-ref ref="LogAllToFile"/>
106       <appender-ref ref="ADONetAppender"/>
107     </root>-->
108   </log4net>
109 </configuration>

在普通的C/S系统下都会有个程序入口,我们可以在这个入口点做文章,在程序启动时添加2个事件来监听异常:

1 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
2 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

这2个事件是当执行Application.Run方法的线程发生未捕获异常时,触发 Application.ThreadException和如果 handler 抛出异常,或者不在 UI 线程中发生异常,将触发
AppDomain.UnhandledException事件。

其入口点代码如下:

01 static class Program
02     {
03         /// <summary>
04         /// 应用程序的主入口点。
05         /// </summary>
06         [STAThread]
07         static void Main()
08         {
09             //若不在AssemblyInfo.cs配置[assembly: log4net.Config.XmlConfigurator(ConfigFile = "WindowsFormsApplication1.exe.config", Watch = true)]
10             //可使用一下代码来读取log4net配置文件
11             string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
12             string assemblyDirPath = Path.GetDirectoryName(assemblyFilePath);
13             string configFilePath = assemblyDirPath + " \\log4net.xml";
14             log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(configFilePath));
15   
16             //异常
17             Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
18             AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
19             Application.EnableVisualStyles();
20             Application.SetCompatibleTextRenderingDefault(false);
21             Application.Run(new Form1());
22         }
23   
24         static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
25         {
26             if (e.ExceptionObject is System.Exception)
27             {
28                 HandleException((System.Exception)e.ExceptionObject);
29             }
30         }
31   
32         static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
33         {
34             HandleException(e.Exception);
35         }
36   
37         public static void HandleException(Exception ex)
38         {
39             log4net.ILog log = (log4net.ILog)log4net.LogManager.GetLogger("LogToSqlite");
40             log.Error(ex.Message, ex);
41         }
42     }

而在B/S程序下面我们则可以利用页面级别的Page_Error事件或者整个应用程序级别的Application_Error事件(本例子中采用Page_Error处理),同时我们还要做一步处理就是在Global.asax中的Application_Start添加log4net的配置读取,或者也可以在AssemblyInfo.cs添加和WinFrom一样的配置,不过如果仅限于WebForm,而Web网站就只能在Global中配置,所以这边我们就采用通用配置:

1 protected void Application_Start(object sender, EventArgs e)
2         {
3             log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(Server.MapPath("~") + @"\log4net.xml"));
4         }

为了方便每个页面进行异常捕获我们建立一个PageBase,让所有的页面都继承这个PageBase,这样只需在PageBase中写一个Page_Error就可以了,代码如下:

01 public class PageBase : System.Web.UI.Page
02     {
03         protected void Page_Error(object sender, EventArgs args)
04         {
05             //获取最新的异常信息
06             var ex = Server.GetLastError();
07             //记录异常信息
08             log4net.ILog log = (log4net.ILog)log4net.LogManager.GetLogger("LogToSqlite");
09             log.Error(ex.Message, ex);
10             //清空异常信息
11             Server.ClearError();
12         }
13     }

这样在页面中就可以进行异常捕获及记录了。具体的代码可以下载查看。

下载地址:点我下载

 在通过上一篇文章:[EntLib]微软企业库5.0 学习之路——第一步、基本入门 对EntLib5.0的相关知识进行了了解,并通过微软给出的学习例子我们可以了解企业库的各模块的基本用法(由于园子里的好几位兄弟都已经把企业库的各模块的使用都介绍过了,所以我这边就不进行具体功能的介绍,我写的学习之路是以企业库如何在一个项目中的使用为基础来介绍,以一个简单的学生信息管理系统项目为基础,如何使用企业库来对这个项目进行开发、优化)。

(本文内容比较简单,高手们可以直接忽略不看了,仅仅就是数据库访问操作和工厂分层)

     现在我就开始进入学习之路的第二步——Data Access模块,这个模块是企业库中被使用频率最高的模块,它很好的封装了数据库操作应用,为我们进行多数据库系统开发提供了便利,只需更改配置文件就可以很快的切换数据库访问(可惜还是要重写SQL语句,没法和ORM比)。

下面是我在配置企业库的时候碰到问题,如果没有碰到可以略去不看(可能有点小白)

注意:此处切换数据库配置必须是计算机中已经安装好相应的数据库访问模块,如需要进行从MS SQL向SQLite数据库的变更时,计算机中必须安装好SQLite数据库访问模块(在这里我就碰到了这个问题,原来我机器上在VS2008开发时已经安装过SQLite数据库访问模块,但是新装了VS2010,在VS2010引用对话框中也能访问到在VS2008安装的SQLite(但是在企业库5.0配置器中无法查看到SQLite),但是发现更改企业库的配置文件后无法访问SQLite数据库,尝试了很多方法都没用,结果死马当活马医又重新装了一遍SQLite数据库访问模块再重新打开企业库配置器就可以看到SQLite数据库了(所以请确保在企业库编辑器中可以查看到要切换的数据库,否则可能导致无法访问数据库)。看下图:

pic6

 

回归正题,这次的学习由于VS2010发布了,而且企业库5.0也都支持.NET4.0,所以决定企业库的学习之路采用VS2010进行学习(顺便熟悉下.NET4的特性,毕竟公司的项目不可能立马转移到.NET4.0的,现在就当练手吧)

好了,现在就开始进行第2步的学习了,首先看下项目的结构:

pic7

项目采用仿MS PetShop架构,如不了解此架构可以到此查看了解:PetShop的系统架构设计

其中DAL和DALSQLite层对应MS SQL和SQLite数据库,Helper为整个项目的帮助器

现在来具体了解下DAL层

pic8

在DAL层中引用了Helper,IDAL,EnterpriseLibrary.Common和EnterpriseLibrary.Data这4个项目,其中Helper项目中有个DBHelper.cs,用于获取当前的数据对象,其代码如下(采用了C#4.0的语法特性,默认参数,数据库对象名默认为空,这样则会调用企业库默认的数据库对象,同时也可以在调用的时候赋值,这样则根据传递过来的数据库对象名来创建数据库,通过这个参数我们将原来需要重载的2个方法才能实现合并成了一个方法):

01 using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
02 using Microsoft.Practices.EnterpriseLibrary.Data;
03   
04 namespace EntLibStudy.Helper
05 {
06     public static class DBHelper
07     {
08         /// <summary>
09         /// 获取数据库对象
10         /// </summary>
11         /// <param name="name">数据库实例名(默认name为空,调用默认数据库实例)</param>
12         /// <returns>数据库对象</returns>
13         public static Database CreateDataBase(string name = "")
14         {
15             //return DatabaseFactory.CreateDatabase(name);
16             return EnterpriseLibraryContainer.Current.GetInstance<Database>(name);
17         }
18     }
19 }

在DAL层中则引用Helper来获取数据库对象,进行数据库操作,我们现在来看下具体的数据库访问类编写代码,学员操作类:

001 using System;
002 using System.Collections.Generic;
003 using System.Data;
004 using System.Data.Common;
005 using System.Linq;
006 using System.Text;
007   
008 using Microsoft.Practices.EnterpriseLibrary.Data;
009   
010 using EntLibStudy.Model;
011 using EntLibStudy.Helper;
012   
013 namespace EntLibStudy.DAL
014 {
015     public class StudentService : EntLibStudy.IDAL.IStudentService
016     {
017         /// <summary>
018         /// 新增学生
019         /// </summary>
020         /// <param name="student">学生对象</param>
021         /// <returns></returns>
022         public int Add(Student student)
023         {
024             Database db = DBHelper.CreateDataBase();
025             StringBuilder sb = new StringBuilder();
026             sb.Append("insert into Student values(@ClassId,@SID,@Password,@Name,@Sex,@Birthday,@IsAdmin);SELECT @@IDENTITY;");
027             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
028             db.AddInParameter(cmd, "@ClassId", DbType.String, student.ClassId);
029             db.AddInParameter(cmd, "@SID", DbType.String, student.Sid);
030             db.AddInParameter(cmd, "@Password", DbType.String, student.Password);
031             db.AddInParameter(cmd, "@Name", DbType.String, student.Name);
032             db.AddInParameter(cmd, "@Sex", DbType.Int32, student.Sex);
033             db.AddInParameter(cmd, "@Birthday", DbType.DateTime, student.Birthday);
034             db.AddInParameter(cmd, "@IsAdmin", DbType.Int32, student.IsAdmin);
035             int id = Convert.ToInt32(db.ExecuteScalar(cmd));
036             return id;
037         }
038   
039         /// <summary>
040         /// 更新
041         /// </summary>
042         /// <param name="classInfo">学生对象</param>
043         /// <returns>是否成功</returns>
044         public bool Update(Student student)
045         {
046             Database db = DBHelper.CreateDataBase();
047             StringBuilder sb = new StringBuilder();
048             sb.Append("update Student set ClassId=@ClassId,");
049             sb.Append("SID=@SID,");
050             sb.Append("Password=@Password,");
051             sb.Append("Name=@Name,");
052             sb.Append("Sex=@Sex,");
053             sb.Append("Birthday=@Birthday,");
054             sb.Append("IsAdmin=@IsAdmin ");
055             sb.Append(" where ID=@ID");
056             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
057             db.AddInParameter(cmd, "@ClassId", DbType.String, student.ClassId);
058             db.AddInParameter(cmd, "@SID", DbType.String, student.Sid);
059             db.AddInParameter(cmd, "@Password", DbType.String, student.Password);
060             db.AddInParameter(cmd, "@Name", DbType.String, student.Name);
061             db.AddInParameter(cmd, "@Sex", DbType.Int32, student.Sex);
062             db.AddInParameter(cmd, "@Birthday", DbType.DateTime, student.Birthday);
063             db.AddInParameter(cmd, "@IsAdmin", DbType.Int32, student.IsAdmin);
064             db.AddInParameter(cmd, "@ID", DbType.Int32, student.Id);
065             return db.ExecuteNonQuery(cmd) > 0 ? true : false;
066         }
067   
068         /// <summary>
069         /// 删除
070         /// </summary>
071         /// <param name="id">学生ID</param>
072         /// <returns>是否成功</returns>
073         public bool Delete(int id)
074         {
075             Database db = DBHelper.CreateDataBase();
076             StringBuilder sb = new StringBuilder();
077             sb.Append("delete from Student ");
078             sb.Append(" where ID=@ID");
079             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
080             db.AddInParameter(cmd, "@ID", DbType.Int32, id);
081   
082             return db.ExecuteNonQuery(cmd) > 0 ? true : false;
083         }
084   
085         /// <summary>
086         /// 根据学生ID查询学生对象
087         /// </summary>
088         /// <param name="id">学生ID</param>
089         /// <returns></returns>
090         public Student SelectById(int id)
091         {
092             Student student = null;
093             Database db = DBHelper.CreateDataBase();
094             StringBuilder sb = new StringBuilder();
095             sb.Append("select * from Student ");
096             sb.Append(" where ID=@ID");
097             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
098             db.AddInParameter(cmd, "@ID", DbType.Int32, id);
099   
100             using (IDataReader reader = db.ExecuteReader(cmd))
101             {
102                 if (reader.Read())
103                 {
104                     student = new Student()
105                     {
106                         Id = reader.GetInt32(0),
107                         ClassId = reader.GetInt32(1),
108                         Sid = reader.GetString(2),
109                         Password = reader.GetString(3),
110                         Name = reader.GetString(4),
111                         Sex = reader.GetInt32(5),
112                         Birthday = reader.GetDateTime(6),
113                         IsAdmin = reader.GetInt32(7)
114                     };
115                 }
116             }
117   
118             return student;
119         }
120   
121         /// <summary>
122         /// 查询所有学生信息
123         /// </summary>
124         /// <returns></returns>
125         public IList<Student> SelectAll()
126         {
127             List<Student> list = new List<Student>();
128             Database db = DBHelper.CreateDataBase();
129             StringBuilder sb = new StringBuilder();
130             sb.Append("select * from Student ");
131             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
132   
133             using (IDataReader reader = db.ExecuteReader(cmd))
134             {
135                 while (reader.Read())
136                 {
137                     list.Add(new Student()
138                     {
139                         Id = reader.GetInt32(0),
140                         ClassId = reader.GetInt32(1),
141                         Sid = reader.GetString(2),
142                         Password = reader.GetString(3),
143                         Name = reader.GetString(4),
144                         Sex = reader.GetInt32(5),
145                         Birthday = reader.GetDateTime(6),
146                         IsAdmin = reader.GetInt32(7)
147                     });
148                 }
149             }
150             return list;
151         }
152   
153         /// <summary>
154         /// 查询所有学生信息
155         /// </summary>
156         /// <returns></returns>
157         public IList<Student> SelectAllMapper()
158         {
159             var list = new List<Student>();
160             Database db = DBHelper.CreateDataBase();
161             DataAccessor<Student> studentAccessor;
162             //studentAccessor = db.CreateSqlStringAccessor("select * from Student",
163             //    MapBuilder<Student>.MapAllProperties().
164             //    Build()
165             //    );
166             studentAccessor = db.CreateSqlStringAccessor("select * from Student",
167                  MapBuilder<Student>.MapAllProperties().
168                  Map(p => p.Id).ToColumn("ID").
169                  Map(p => p.Sid).ToColumn("SID").
170                  Map(p => p.Password).WithFunc(f => "******").//将密码转换为"*",无法直接查看
171                  Map(p => p.Name).WithFunc(ToUpperName).//将学员名称转换为大写
172                  Map(p => p.Sex).ToColumn("Sex").
173                  Map(p => p.Birthday).ToColumn("Birthday").
174                  Build()
175              );
176             list = studentAccessor.Execute().ToList();
177             return list;
178         }
179   
180         /// <summary>
181         /// 将学员名称转换为大写
182         /// </summary>
183         /// <param name="dataRecord"></param>
184         /// <returns></returns>
185         private string ToUpperName(IDataRecord dataRecord)
186         {
187             var name = (string)dataRecord["Name"];
188             return name.ToUpper();
189         }
190   
191         public Student SelectBySid(string sid)
192         {
193             Student student = null;
194             Database db = DBHelper.CreateDataBase();
195             StringBuilder sb = new StringBuilder();
196             sb.Append("select * from Student ");
197             sb.Append(" where SID=@SID");
198             DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
199             db.AddInParameter(cmd, "@SID", DbType.String, sid);
200   
201             using (IDataReader reader = db.ExecuteReader(cmd))
202             {
203                 if (reader.Read())
204                 {
205                     student = new Student()
206                     {
207                         Id = reader.GetInt32(0),
208                         ClassId = reader.GetInt32(1),
209                         Sid = reader.GetString(2),
210                         Password = reader.GetString(3),
211                         Name = reader.GetString(4),
212                         Sex = reader.GetInt32(5),
213                         Birthday = reader.GetDateTime(6),
214                         IsAdmin = reader.GetInt32(7)
215                     };
216                 }
217             }
218   
219             return student;
220         }
221   
222     }
223 }

其中的代码都是采用了比较常见的老套路:

1、获取数据库对象

2、构建Command对象并进行执行语句及参数赋值

3、通过数据库对象调用相应方法执行Command

企业库在Data Access上帮我们做了比较好的封装,相当于为我们提供了如SQLHelper,OracleHelper类,只不过这个帮助类转换了一个个数据库的对象,通过数据库对象来对数据库数据进行操作

(个人认为通过这种方式进行操作更加直观,而且企业库的对SQL语句的参数操作方法也很直观:AddInParameter,AddOutParameter,GetParameterValue很好的区分了参数的操作,比原来的SQLCommand好多了)

如果仔细看了上面操作代码的朋友肯定发现了类中有个叫SelectAllMapper的方法,这个方法采用的是企业库5.0中新提供的Accessor进行RowMapper来直接为实体赋值,相比原来的使用reader方式取值赋值更加优雅,只要SQL查询出来的对象字段和实体对象属性一样就可以使用MapAllProperties()方法直接赋值,如果不同的话可以使用map方法来对个别属性单独映射,而且在映射的时候还可以使用WithFunc来进行进一步操作,在代码中我将密码进行了替换,以“*”的形式展示,同时把学员的名称以大写的形式展示。

(注:更多的企业库Data Access模块方法使用可以点击这里下载微软给出的学习例子和http://www.entlib.com/发布的学习手册)

在完成底层的操作,现在我们就开始对企业库的数据库访问进行配置:

在Web层的Web.config上右键打开企业库配置器:Blocks-Add Data Settings-Add DataBase Connstring,新建2个数据库对象,一个是MS SqlServer数据库,一个是SQLite数据库.

 

pic9

新建一个数据库设置

pic10

新建二个数据库连接

pic11

一个为EntLibStudy,另一个为EntLibSQLite

我们来看下具体的配置文件代码:

01 <configuration>
02   <configSections>
03     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
04   </configSections>
05     
06   <dataConfiguration defaultDatabase="EntLibStudy" />
07   <connectionStrings>
08     <add name="EntLibStudy" connectionString="server=VOLK\SQL2005;database=EntLibStudy;Integrated Security=True;"
09       providerName="System.Data.SqlClient" />
10     <add name="EntLibStudySQLite" connectionString="data source=|DataDirectory|EntLibStudySQLite.db3"
11       providerName="System.Data.SQLite" />
12   </connectionStrings>
13     
14   <appSettings>
15     <add key="DAL" value="EntLibStudy.DAL" />
16   </appSettings>
17     
18   <system.web>
19     <compilation debug="true" targetFramework="4.0" >
20       <expressionBuilders>
21         <add expressionPrefix="RouteUrl" type="System.Web.Compilation.RouteUrlExpressionBuilder"/>
22         <add expressionPrefix="RouteValue" type="System.Web.Compilation.RouteValueExpressionBuilder"/>
23       </expressionBuilders>
24     </compilation>
25     <authentication mode="Forms">
26     </authentication>
27   
28   </system.web>
29   
30 </configuration>

至此我们就完成了Data Access模块的代码编写和基本设置(具体代码请到文章底部下载源代码,类似代码则不再描述)。

这时如果项目需求发生了变更,由于成本太高不能使用MS SQL SERVER,而要改用SQLite数据库时则只需更改配置文件,将dataConfiguration配置节中defaultDatabase更改为EntLibStudySQLite,将appSettings配置节中DAL的值改为EntLibStudy.DALSQLite即可立刻切换到SQLite数据库。

下面我来看下运行出来的效果:

 pic12

红色框中地址采用了ASP.NET4中的路由功能,实现了地址重写

pic13

红框中如上面的分析,在DAL层进行属性映射的时候已经将密码以*代替,学员姓名以大写形式展现(此页面仅用来展示属性映射,无其他用处,页面地址为:~/Student/StudentList.aspx)

至此,学习之路的第二步——Data Access模块的学习就到此为止了,其他一些东西请下载源代码查看。

注意:

1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。

2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。

3、项目开发环境为:VS2010+SQL2005。

4、管理员帐户:admin

              密码:admin

项目下载地址:点我下载

话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白)。

      直到去年在做项目的时候在看代码生成工具的时候看到了许多生成工具都内置了企业库的数据库访问及一些相关模块,才突然顿悟了,可能企业库应该还是有点用的,于是就试着使用了企业库的一些模块(当时版本是4.1),果然觉得企业库还是很不错的,微软在企业库中为我们封装了许多日常经常使用的模块,如:数据库访问,缓存,日志,异常处理,加密,IOC等等,在日常的项目开发中,企业库已经贯穿了一个项目从底层到表示层基本上需要用到的模块,大大方便了我们的开发,毕竟自己再重复造轮子还不如就用微软的(毕竟企业库现在都到5.0)

      最近在看园子里有好几位都在写,如:virus,huangcong。我现在写的其实基本上都是自己的学习之路(可能没前面2位写的好,但就当自己的学习笔记吧)。

      废话不多说了,首先开始学习企业库的基本概念,一下摘自sfbirp的企业库学习笔记:

      一、什么是企业库?

      企业库包含一系列应用程序模块和核心架构。这些高复用的组件旨在帮助开发者解决一些共同的企业开发问题。
企业库同时提供高可配置的特性,使我们管理重复的工作更加容易,例如我们知道的在应用的很多地方发生的横切关注点。包括像日志记录、缓存、异常管理等。另外,它提供的依赖注入容器能够简化和分解你的设计,使他们更加可实验和容易理解,帮助你创建更加高性能的设计和各种应用。
     二、使用企业库的3个简单步骤:
     1.引用和配置你需要的程序集。
     2.通过企业库配置应用。
     3.在你的代码中创建和使用企业库对象。
      三、企业库的好处:
      应用模块帮助解决开发者从一个项目到另一个项目面对的共同问题。他们的设计封装了微软推荐的做法,这些都是基于微软框架的应用开发。例如,数据访问应用模块提供了对ADO.NET访问最频繁使用的特征。在某些情况下,应用模块还添加了一些基础类库没有直接提供的相关功能。
      四、企业库的目标
     1.一致。所有的企业库模块都坚持一致的设计模式和实现方式。
     2.可扩展性。所有的应用模块包括定义扩展点,允许开发人员通过添加自己的代码定制应用模块的行为。
     3.易用性。企业库提供了许多实用性的东西,包括一个图形化配置工具,简单的安装过程,完成的文档和示例。
     4.集成。企业库应用模块被设计得能够一起很好的工作,并且也被这样测试过。但是你不必一起使用他们。我们可以单独使用这些应用模块,同时这些应用模块之间也有些依赖,比如在企业库核心和Unity中的一些通用组件。

      上面是企业库的基本概念,理解了企业库的相关知识后,我们可以开始来安装企业库了

   1、下载地址:点我进入下载页面(不是直接下载),安装后就可以使用了。

        这次5.0相比4.1的最大改动就是整个配置工具采用了WPF重新构建和实例化和管理对象生命周期的全依赖注入的实现,同时支持VS2008SP1和VS2010,话说虽然这次的配置工具变化挺大的,但是一旦熟悉了就觉得比4.1的好,因为可以清楚的看见每个模块的之间的依赖关系

        附相关改动,还是摘自sfbirp的企业库学习笔记:

一、Unity和对象生成器的整合
     在这个版本中,用于创建对象的基本技术是一个单一的依赖注入容器,默认的是Unity。你可以使用容器生成企业库对象的实例并注入到其他的对象。 企业库提供一个标准的接口从Unity容器中来获得定义在企业库配置中的对象的实例,如SqlDatabase或LogWriter.另外,注入友好的实例门面处理静态门面之外是有效的,因为静态门面不能用注入,但是为了向后兼容以前的版本而存在。在本版本中的示例中都是用依赖注入,但是以前版本中是用的静态工厂类和静态门面在这个版本中还是支持的。对象生成器,一个低版本的依赖注入机制在这个版本中被归入Unity中,在项目中不再需要单独引用对象生成器集。
      支持Unity容器,必须引用一个被包含在企业库中的Microsoft.Practices.ServiceLocation程序集。如果要使用一个第3方的依赖注入容器,必须直接实现IServiceLocator接口或者通过适配器实现。

二、影响所有模块的变化:
     1.在企业库中主要修正是使用依赖注入机制。所用的应用模块以及核心系统都是用依赖注入机制,并使用Unity作为默认的依赖注入容器,来创建和管理企业库对象。
     2.自主容器的实现通过实现Common Service Locator项目提供的 IServiceLocator 接口来完成。
     3.由于错误配置引发的错误将提供更多有用的错误信息。
     4.配置系统公开了一个 fluent接口,用来为单个对象或整个应用创建和填充配置源。fluent API使得为各种情景创建配置源更加容易。
     5.ConfigurationView类被删除.
     6.一些配置元素有默认值,可能不同于以前版本的隐式默认值.
     7.企业库现在允许你通过另一个配置文件合成一个混合配置文件.
     8.可以可通过不同的配置文件读取不同的配置信息.
     9.企业库不支持XAML浏览器应用程序(XBAP).
     10.WmiEnabled标志为了像前兼容仍然存在企业库中,但是在5.0中是被忽略的,而且将来会被删除.
     11.改进式的安装允许你只安装部分应用模块及配置工具.
     12.在以前版本中要做统一集成,必须添加核心企业库的扩展和每个模块的扩展。现在如果你只需要直接访问容器,那么只有核心扩展是必须的。单独模块将自动支持。
旧的功能为了保持像前兼容仍然保留,但已经不起作用。
     13.FileConfigurationSource.Save 的签名已经改变,参数由3个变为2个。
     14.快速入门不再包含在主安装程序中。

三、Breaking变化:
     1.企业库现在抛出了一个配置错误ActivationException,之前是System.Configuration.ConfigurationErrorsException。这包括试着解决没有配置信息错误的一个实例提供者。
     2.以前版本在获取应用模块错误时抛出BuildFailedException错误,现在对于所有的应用模块都抛出ActivationException
     3 .之前的版本,在讲一个空源传到容器来调用容器的时候,会抛出ArgumentNullException,现在抛出NullReferenceException
     4.ObjectBuilder2不再是一个单独的程序集,而是集成到了Unity集合中,在项目中也不需要引用ObjectBuilder2.dll。

     5.WMI支持已经从企业库中删除,除了在logging模块中的WMI跟踪监听器。
     6.如果你没有关闭DbDataReader,可能会导致随机的、很难在您的代码中找到的错误,尤其是当你正在一个由TransactionScope上下文创建的隐式事务下操作时。
你必须始终确保您的应用程序及时关闭DbDataReader,无论是明确的DbDataReader.Close方法关闭或是逼迫DbDataReader释放。
      7.如果你使用 validator 特性必须引用 System.ComponentModel.DataAnnotations程序集。
      8.为FileConfigurationSource.Save方法签名已更改。该方法有两个参数,而不是3个参数
      9.Microsoft.Practices.EnterpriseLibrary.Configuration.Design.dll集合的功能和其他设计时集合被一个新的集合Microsoft.Practices.EnterpriseLibrary.Configuration.DesignTime.dll代替。
      10,性能计数器异常从PolicyInjection.CallHandlers移到 PolicyInjection 程序集。
      11.包含在Policy Injection Application Block中的CachingCallHandler有未处理的安全漏洞,已经从Policy Injection Application Block中移除。

四、配置工具的改变:
      1.新的企业拥有一个新的GUI库和一个元数据驱动的可扩展性模。
      2.支持向导
      3.新的类型选择。
      4.不支持对依赖策略的Environmental Overrides 。日志模块处理Categories。
五、缓存模块变化:
      1.缓存清除已被完全重写的性能优化
六、数据库访问模块:
      1.ExecuteReader, ExecuteXmlReader, ExecuteScalar, and ExecuteNonQuery方法具有异步版本。
      2.包含了很多新的方法和类允许你提取数据作为对象序列。例如在合适的时候使用客户端查询技术,如LINQ.
      3.存在的方法ExecuteReader和新的方法BeginExecuteReader不接收CommandBehavior 参数。默认的当调用这些方法的时候这些方法
会自动设置CommandBehavior 属性到reder中用来关闭连接直到指定一个事务。

七、异常处理模块:
      1.日志异常处理重新使用日志模块的Log Writer and Trace Listeners 。这在之前版本中不是默认设置。
      2.增加一个功能,通过ExceptionManager.Process 方法接收一个默认值并返回一个值。

    在安装完企业库同时知道了5.0的变化后,就开始正式的学习了,学习的最好办法不是看企业库的源代码(我是看不懂),而是下载微软发布的学习例子

    学习例子下载地址:点我进入下载页面(不是直接下载)

    这个学习例子里面已经包含了所需的类库及代码演示(C#和VB.NET都有),同时可以右键例子中的App.config来看下例子中的模块是怎么配置的。

    同时,在学习例子的时候可以看下由http://www.entlib.com/发布的中文学习手册,虽然是4.1的,但是里面的所有内容都和5.0的例子一样(5.0只不过是重新实现了1遍),把例子和文档结合起来看学习起来就快了很多了:

     中文学习手册:点我进入下载页面(不是直接下载)

到此,Entlib5的学习之路的第一步就结束了,基本上通过微软发布的学习例子和对应的中文学习手册可以对5.0的使用已经有了个基本的了解。

作者:kyo-yo
出处:http://kyo-yo.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted on 2010-07-01 15:16  HackerVirus  阅读(4750)  评论(1编辑  收藏  举报