• 使用EnterpriseLibrary的PIAB与Unity搭建简单AOP框架


            首先声明,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

  • 相关阅读:
    树状数组
    Windows系统重装
    桶排序
    PCL+VS2010环境配置
    刷题
    杭电ACM——自我强化步骤
    杭电ACM题单
    centos7 ifconifg没有ip
    centos7切换图像界面和dos界面
    oracle with as 的用法
  • 原文地址:https://www.cnblogs.com/wintersun/p/1753533.html
Copyright © 2020-2023  润新知