首先声明,Enterprise Library v5.0已发布了,此处的DEMO是基于Enterprise Library v4.1这个版本. 差别并不太多了.Enterprise Library 提供了图形的化配置工具,让我们免去写XML的配置文件.我们可以用policy injection application block和Unity搭建一个简单的AOP框架,而你不用写一句代码. AOP框架解决是实现Logger,Transaction,同步处理,权限控制,验证等功能的复用,让我们把更多的精力关注在业务上.

         使用配置工具分别增加Exception Handing Application Block,Logging Application Block, Policy Injection Application Block如下图:

PIAB_shot

       PIAB的Matching Rules是很灵活的,可以匹配一个Type,Namespace,Assembly等.这里配置的是Tag Attribute,当然还可以配置自定义规则.为PIAB的Handlers增加Exception Handling,Logging,TransactionScope handler,注意这个TransactionScope是社区贡献的,去Enterprise Library Contrib project下载. 这里你可能还注意到上面的Exception Handing Application Block也有一个Logging Handler, 不是同一个,但它们可以共享Logging Application Block的Category.配置时是非常灵活的.最终的XML是:

  <configSections>
    <section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <policyInjection>
    <policies>
      <add name="MyPIPolicy">
        <matchingRules>
          <add match="MyTagName" ignoreCase="false" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TagAttributeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            name="Tag Attribute Matching Rule" />
        </matchingRules>
        <handlers>
          <add exceptionPolicyName="MyPolicy" order="2" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.ExceptionCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            name="Exception Handling Handler" />
          <add logBehavior="BeforeAndAfter" beforeMessage="MyDefault Before"
            afterMessage="MyDefault After&#xD;&#xA;" eventId="0" includeParameterValues="true"
            includeCallStack="true" includeCallTime="true" priority="-1"
            severity="Information" order="0" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            name="Logging Handler">
            <categories>
              <add name="General" />
            </categories>
          </add>
          <add transactionScopeOption="Required" timeout="00:01:00" isolationLevel="Serializable"
            interopOption="None" complete="true" type="EntLibContrib.PolicyInjection.CallHandlers.TransactionScopeCallHandler, EntLibContrib.PolicyInjection.CallHandlers, Version=4.1.0.0, Culture=neutral, PublicKeyToken=null"
            name="TransactionScope Handler" />
        </handlers>
      </add>
    </policies>
  </policyInjection>
  <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
      <add fileName="Generaltrace.log" header="----------------------------------------"
        footer="----------------------------------------" formatter="Text Formatter"
        listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="FlatFile TraceListener" />
      <add fileName="ErrorRolling.log" footer="----------------------------------------"
        formatter="" header="----------------------------------------"
        rollFileExistsBehavior="Overwrite" rollInterval="None" rollSizeKB="0"
        timeStampPattern="yyyy-MM-dd" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="Rolling Flat File Trace Listener" />
    </listeners>
    <formatters>
      <add template="Timestamp: {timestamp}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
        type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="Text Formatter" />
    </formatters>
    <categorySources>
      <add switchValue="All" name="ExceptionLogger">
        <listeners>
          <add name="Rolling Flat File Trace Listener" />
        </listeners>
      </add>
      <add switchValue="All" name="General">
        <listeners>
          <add name="FlatFile TraceListener" />
        </listeners>
      </add>
    </categorySources>
    <specialSources>
      <allEvents switchValue="All" name="All Events" />
      <notProcessed switchValue="All" name="Unprocessed Category" />
      <errors switchValue="All" name="Logging Errors &amp; Warnings">
        <listeners>
          <add name="Rolling Flat File Trace Listener" />
        </listeners>
      </errors>
    </specialSources>
  </loggingConfiguration>
  <exceptionHandling>
    <exceptionPolicies>
      <add name="MyPolicy">
        <exceptionTypes>
          <add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            postHandlingAction="NotifyRethrow" name="Exception">
            <exceptionHandlers>
              <add logCategory="ExceptionLogger" eventId="100" severity="Error"
                title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                priority="0" useDefaultLogger="false" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                name="Logging Handler" />
            </exceptionHandlers>
          </add>
        </exceptionTypes>
      </add>
    </exceptionPolicies>
  </exceptionHandling>

需要引用以下程序集:

   1:    <Reference Include="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
   2:      <Reference Include="Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
   3:      <Reference Include="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
   4:      <Reference Include="Microsoft.Practices.Unity, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
   5:      <Reference Include="Microsoft.Practices.Unity.Interception, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
   6:     <Reference Include="EntLibContrib.PolicyInjection.CallHandlers, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">

好了,一切就绪了. 让我们来测试下:

   1:      public interface IProdutDAO 
   2:      {
   3:          bool AddProduct(string productname);
   4:          bool DeleteProduct(int pkid);
   5:          bool UpdateProduct(int pkid);
   6:      }
   7:   
   8:     /// <summary>
   9:      /// ProductDAO
  10:      /// </summary>
  11:      /// <remarks>author: Petter Liu http://wintersun.cnblogs.com </remarks>
  12:      public class ProductDAO : IProdutDAO
  13:      {
  14:         [ExceptionCallHandler("MyPolicy")]
  15:          public bool AddProduct(string productname)
  16:          {
  17:              throw new Exception("You can not create duplicate user in system");
  18:          }
  19:   
  20:          [LogCallHandler]
  21:         public bool DeleteProduct(int pkid)
  22:         {
  23:             return true;
  24:         }
  25:   
  26:          [TransactionScopeCallHandler]
  27:          [ExceptionCallHandler("MyPolicy")]
  28:          public bool UpdateProduct(int pkid)
  29:          {
  30:              string sql = @"delete Employees where EmployeeID=(select max(EmployeeID) from Employees where LastName='Petter')
  31:                                 Insert into Employees(LastName,FirstName,Title) values('testname','asddddddddddddddddddddddasdfasdffsd','fsfs');";
  32:   
  33:              ConnectionStringSettings cfg = ConfigurationManager.ConnectionStrings["maindb"];
  34:              DbProviderFactory factory = DbProviderFactories.GetFactory(cfg.ProviderName);
  35:             
  36:              using (DbConnection cnx = factory.CreateConnection())
  37:              {
  38:                  using (DbCommand cmd = factory.CreateCommand())
  39:                  {
  40:                      cnx.ConnectionString = cfg.ConnectionString;
  41:                      cmd.Connection = cnx;
  42:                      cmd.CommandText = sql;
  43:             
  44:                      cnx.Open();
  45:                      int roweffect = cmd.ExecuteNonQuery();
  46:                      cnx.Close();
  47:                      cmd.Dispose();
  48:                  }
  49:              }
  50:              return false;
  51:          }
  52:      }

这里使用是Attribute, 看下使用Tag attribute的:

   1:      /// <summary>
   2:      /// All method will be blocked
   3:      /// </summary>
   4:      [Tag("MyTagName")]
   5:      public class ProductLogDAO : IProdutDAO
   6:      {
   7:          public bool AddProduct(string productname)
   8:          {
   9:              throw new Exception("You can not create duplicate user in system");
  10:          }
  11:   
  12:          #region IProdutDAO Members
  13:   
  14:          public bool DeleteProduct(int pkid)
  15:          {
  16:              return true;
  17:          }
  18:   
  19:          #endregion
  20:   
  21:          #region IProdutDAO Members
  22:   
  23:          public bool UpdateProduct(int pkid)
  24:          {
  25:              return false;
  26:          }
  27:   
  28:          #endregion
  29:      }

我们的UnitTest Code如下:

   1:      /// <summary>
   2:      /// TestPIAB
   3:      /// </summary>
   4:      /// <remarks>Author Petter Liu http://www.cnblogs.com/wintersun </remarks>
   5:      [TestFixture]
   6:      public class TestPIAB
   7:      {
   8:          [Test]
   9:          [ExpectedException(typeof(Exception))]
  10:          public void TestThrowException()
  11:          {
  12:              IProdutDAO productdao = PolicyInjection.Create<ProductDAO, IProdutDAO>();
  13:              productdao
  14:                  .AddProduct("new product");
  15:   
  16:          }
  17:   
  18:          [Test]
  19:          public void TestAutoLogger()
  20:          {
  21:              IProdutDAO productdao = PolicyInjection.Create<ProductDAO, IProdutDAO>();
  22:              productdao.DeleteProduct(1);
  23:   
  24:          }
  25:   
  26:          [Test]
  27:          public void TestAutoTransaction()
  28:          {
  29:              IProdutDAO productdao = PolicyInjection.Create<ProductDAO, IProdutDAO>();
  30:              productdao.UpdateProduct(1);
  31:          }
  32:   
  33:          [Test]
  34:          [ExpectedException(typeof(Exception))]
  35:          public void TestTagAttribute()
  36:          {
  37:              IProdutDAO productdao = PolicyInjection.Create<ProductLogDAO, IProdutDAO>();
  38:              productdao
  39:                  .AddProduct("new product");
  40:           
  41:          }
  42:   
  43:          [Test]
  44:          [ExpectedException(typeof(Exception))]
  45:          public void TestWorkingWithUntiy()
  46:          {
  47:              IUnityContainer container = new UnityContainer();
  48:              container.RegisterType<IProdutDAO, ProductDAO>();
  49:              container.AddNewExtension<Interception>();
  50:              container.Configure<Interception>().SetDefaultInterceptorFor(typeof(IProdutDAO)
  51:                  , new TransparentProxyInterceptor());
  52:              var productdao = container.Resolve<IProdutDAO>();
  53:              productdao
  54:                .AddProduct("new product");
  55:          }
  56:      }

当我们执行第一个AddProduct时, 将会产生这样的日志ErrorRolling.log, 这是自动Exception Logging:

----------------------------------------
ExceptionLogger Error: 100 : Timestamp: 2010-06-07 14:14:54
Message: HandlingInstanceID: d684dfc6-0e5a-476d-861f-9c685b818ad3
An exception of type 'System.Exception' occurred and was caught.
----------------------------------------------------------------
06/07/2010 22:14:54
Type : System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : You can not create duplicate user in system
Source : WindowsFormsApplication1
Help link : 
Data : System.Collections.ListDictionaryInternal
TargetSite : Boolean AddProduct(System.String)
Stack Trace :    at WindowsFormsApplication1.ProductDAO.AddProduct(String productname) in H:\My Project\DotNet30\WindowsFormsTDD2008\TestPIAB.cs:line 113

Additional Info:

MachineName : USER
TimeStamp : 2010-06-07 14:14:54
FullName : Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
AppDomainName : domain-nunit.addin.dll
ThreadIdentity : 
WindowsIdentity : USER\Petter

Category: ExceptionLogger
Priority: 0
EventId: 100
Severity: Error
Title:Enterprise Library Exception Handling
Machine: USER
App Domain: domain-nunit.addin.dll
ProcessId: 6076
Process Name: D:\Program Files\TestDriven.NET 2.0\ProcessInvocation.exe
Thread Name: TestRunnerThread
Win32 ThreadId:2176
Extended Properties: 
----------------------------------------

当我们执行DeleteProduct, 测试的是自动方法Logging, 此处将生成日志文件:Generaltrace.log,有BeforeMessage,AfterMessage,所以有两段
借这个思路,我们可以实现前方法,后方法,以后有时间讨论.内容如下:

----------------------------------------
Timestamp: 2010-06-07 14:22:10
Message: 
Category: General
Priority: -1
EventId: 0
Severity: Information
Title:Call Logging
Machine: USER
Application Domain: domain-nunit.addin.dll
Process Id: 6076
Process Name: D:\Program Files\TestDriven.NET 2.0\ProcessInvocation.exe
Win32 Thread Id: 6412
Thread Name: TestRunnerThread
Extended Properties: pkid - 1

----------------------------------------
----------------------------------------
Timestamp: 2010-06-07 14:22:10
Message: 
Category: General
Priority: -1
EventId: 0
Severity: Information
Title:Call Logging
Machine: USER
Application Domain: domain-nunit.addin.dll
Process Id: 6076
Process Name: D:\Program Files\TestDriven.NET 2.0\ProcessInvocation.exe
Win32 Thread Id: 6412
Thread Name: TestRunnerThread
Extended Properties: pkid - 1

----------------------------------------
当我们执行测试UpdateProduct时,这时测试的是自动Transaction,我们故意执行SQL时,先删除一条记录,再插入一条记录,
并造成字段截段的Exception,看以下日志的结果:
ExceptionLogger Error: 100 : Timestamp: 2010-06-07 14:22:03
Message: HandlingInstanceID: 59690aa2-8e07-4bdb-bcfc-28379961e667
An exception of type 'System.Data.SqlClient.SqlException' occurred and was caught.
----------------------------------------------------------------------------------
06/07/2010 22:22:03
Type : System.Data.SqlClient.SqlException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : 将截断字符串或二进制数据。
语句已终止。
Source : .Net SqlClient Data Provider
Help link : 
Errors : System.Data.SqlClient.SqlErrorCollection
Class : 16
LineNumber : 2
Number : 8152
Procedure : 
Server : .\sqlexpress2008
State : 4
ErrorCode : -2146232060
Data : System.Collections.ListDictionaryInternal
TargetSite : Void OnError(System.Data.SqlClient.SqlException, Boolean)
Stack Trace :    at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at WindowsFormsApplication1.ProductDAO.UpdateProduct(Int32 pkid) in H:\My Project\DotNet30\WindowsFormsTDD2008\TestPIAB.cs:line 141

Additional Info:

MachineName : USER
TimeStamp : 2010-06-07 14:22:03
FullName : Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
AppDomainName : domain-nunit.addin.dll
ThreadIdentity : 
WindowsIdentity : USER\Petter

Category: ExceptionLogger
Priority: 0
EventId: 100
Severity: Error
Title:Enterprise Library Exception Handling
Machine: USER
App Domain: domain-nunit.addin.dll
ProcessId: 6076
Process Name: D:\Program Files\TestDriven.NET 2.0\ProcessInvocation.exe
Thread Name: TestRunnerThread
Win32 ThreadId:7532
Extended Properties: HelpLink.ProdName - Microsoft SQL Server
HelpLink.ProdVer - 10.00.1600
HelpLink.EvtSrc - MSSQLServer
HelpLink.EvtID - 8152
HelpLink.BaseHelpUrl - http://go.microsoft.com/fwlink
HelpLink.LinkId - 20476

----------------------------------------
是不是相当详细的,记录的内容模板你是可以修改的,这里全部用的是默认的配置值.
TestTagAttribute 这里使用是Tag Attribute,那被标记的对像将按配置的Order值执行所有Injection Handler. 
以最后一个UnitTest方法是PIAB与Unity组合使用.
 
总结,现在是组件开发的时代了,上面所有组件你都可以使用其它的,或是你自己写的,或是第三方的.使用Enterprise Libary各个组件,我们可以轻易搭建一个AOP框架.


作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog

posted on 2010-06-07 22:42  PetterLiu  阅读(4573)  评论(4编辑  收藏  举报