程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成

Enterprise Library深入解析與靈活應用(7):再談PIAB與Unity之間的集成

編輯:關於.NET

在EnteLib中,PIAB(Policy Injection Application Block)和Unity的定位 是輕量級的AOP框架和IoC容器(Container)。通過PIAB,我們可以將一些業務無 關的crosscutting concern定義於相應的CallHandler中,通過Attribute聲明或 者配置應用到承載業務邏輯的目標方法上。而通過Unity提供的IoC容器(或者DI 容器),即UnityContainer,很好地實現了依賴的動態注入,從而實現了組件之 間、模塊之間或者服務之間的松耦合。

Unity完全建立在ObjectBuilder2 之上,顧名思義,這是一個用於創建對象的基礎組件。ObjectBuilder2提供了一 種具有高可擴展性的、基於策略(Strategy Based)的對象創建框架,它不僅僅 是Unity的基礎組件,也是整個EnterLib和Software Factory的基石。而PIAB通過 方法調用劫持(Method Call Interception)的機制實現了策略注入(Policy Injection)。PIAB提供了不同的方法劫持機制,最為典型的就是基於 TransparentProxy(可以參考我的PIAB系列文章)和代碼生成(比如動態生成一 個繼承自目標類型的子類,通過Override掉相應的Virtual方法實現策略注入;或 者動態生成一個實現了目標接口的類型,實現相應的方法實現策略注入)。PIAB 需要通過特殊的機制創建可被劫持(Interceptable)對象,而UnityContainer本 質上是一個創建對象的容器,如果能夠使UnityContainer按照PIAB的要求創建可 被劫持(Interceptable)對象,那麼就能實現兩者之間的集成。(Source Code 從這裡下載)

一、Unity 1.2和EnterLib 4.1如何實現兩者的集成

我在本系列的第一篇文章就談過PIAB和Unity之間的集成問題,當時我們是采用了 一個自定以UnityContainerExtension實現的,當時針對的版本是Unity 1.1和 EnterLib 3.1。到了Unity 1.2和EnterLib 4.1,Unity已經被廣泛地使用到了整 個EnterLib內部,微軟甚至通過Unity對PIAB進行了徹底的改造。所以,最新的 Unity和PIAB中,已經提供了兩者的原生集成。

Unity和PIAB兩者之間的集 成是通過一個特殊的 UnityContainerExtension——Microsoft.Practices.Unity.Intercep tionExtension.Interception實現的。為了演示Interception的使用,我們創建 一個簡單的例子。該例子中定義了一服務SyncTimeProvisionService用於實現同 步時間的提供,SyncTimeProvisionService實現了接口ISyncTimeProvision。 SyncTimeProvisionService本身並不提供具體實現,而是通過另一個組件 SyncTimeProvider實現具體的同步時間的返回,SyncTimeProvider實現接口 ISyncTimeProvider。

你可以將SyncTimeProvisionService和 SyncTimeProvider看成是一個應用中具有依賴關系的兩個模塊,為了實現兩個模 塊之間的解耦,采用基於接口的依賴是推薦的設計模式。所以, SyncTimeProvisionService並不之間依賴於SyncTimeProvider,而是依賴於 SyncTimeProvider的接口ISyncTimeProvider。我們通過Constructor注入實現依 賴注入。為了讓讀者對Unity和PIAB集成的效果具有一個直觀的印象,我在 SyncTimeProvider 上應用了一個CachingCallHandlerAttribute,如果該 CallHandler生效,方法執行的結果將會被緩存,在緩存過期之前,得到的時間將 是一樣的。相應的代碼如下所示:

using System;
namespace Artech.InterceptableUnity
{
  
  public  interface ISyncTimeServiceProvision
  {
    DateTime  GetCurrentTime();
  }
  public class  SyncTimeServiceProvisionService : ISyncTimeServiceProvision
   {
    public ISyncTimeProvider SyncTimeProvider
     { get; private set; }
    public  SyncTimeServiceProvisionService([Dependency]ISyncTimeServiceProvider  syncTimeServiceProvider)
    {
       this.SyncTimeServiceProvider = syncTimeServiceProvider;
     }
    #region ISyncTimeServiceProvision Members
     public DateTime GetCurrentTime()
    {
       return this.SyncTimeProvider.GetCurrentTime();
    }
     #endregion
  }
  public interface  ISyncTimeProvider
  {
    DateTime GetCurrentTime ();
  }
  [CachingCallHandler]
  public class  SyncTimeProvider : ISyncTimeProvider
  {
    #region  ISyncTimeServiceProvider Members
    public DateTime  GetCurrentTime()
    {
      return  DateTime.Now;
    }
    #endregion
  }
}

那麼我們就可以通過下面的方式,利用UnityContainer采用基於 接口(ISyncTimeServiceProvision)的方式創建 SyncTimeServiceProvisionService ,並調用GetCurrentTime方法:

using System;
using System.Threading;
using  Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;
using Microsoft.Practices.Unity;
using  Microsoft.Practices.Unity.InterceptionExtension;
namespace  Artech.InterceptableUnity
{
  class Program
  {
    static void Main(string[] args)
    {
       IUnityContainer container = new UnityContainer();
       container.RegisterType<ISyncTimeServiceProvision,  SyncTimeServiceProvisionService>();
       container.RegisterType<ISyncTimeProvider, SyncTimeProvider> ();
      container.AddNewExtension<Interception> ();
      container.Configure<Interception> ().SetDefaultInterceptorFor(typeof(ISyncTimeServiceProvider), new  TransparentProxyInterceptor());
      var  syncTimeServiceProvision =  container.Resolve<ISyncTimeServiceProvision>();
       for (int i = 0; i < 5; i++)
      {
         Console.WriteLine(syncTimeServiceProvision.GetCurrentTime ());
        Thread.Sleep(1000);
      }
    }
  }
}

通過下面的輸出,我們看出輸出的 時間都是相同的,從而證實了CachingCallHandlerAttribute的有效性,進而正式 了UnityContainer和PIAB的集成:

二、通過自定義 UnityContainerExtension的方式實現Unity與PIAB的集成

通過 Microsoft.Practices.Unity.InterceptionExtension.Interception對Unity和 PIAB兩者之間的集成,需要我們借助Interception為每一個需要被劫持 (Interception)的類型注冊相應的Interceptor(實現接口 Microsoft.Practices.Unity.InterceptionExtension.IInterceptor),如下面 的代碼片斷所示。

container.Configure<Interception> ().SetDefaultInterceptorFor(typeof(ISyncTimeServiceProvider), new  TransparentProxyInterceptor()); 

但是在每多情況下,我們不可能預 先確定需要注冊哪些對象,或者這樣的類型很多,手工注冊的方式將不具有可操 作性。比如,在一個N-Layer的應用中,上層的對象通過UnityContainer創建下層 對象,並且通過PIAB的方式將不同的Crosscutting Concern應用於相應的層次, 我們不可能對每一個應用了PAIB CallHandler相關的類型進行Interceptor的注冊 。

為此,我對Interception進行了擴展,實現了Interceptor的動態注冊 。Unity采用兩種不同的InterceptionStrategy:InstanceInterceptionStrategy 和TypeInterceptionStrategy,它們分別采用基於InstanceInterceptor和 TypeInterceptor實現方法調用劫持。我繼承了InstanceInterceptionStrategy和 TypeInterceptionStrategy,將Inteceptor的動態注冊定義在PreBuildUp方法中 。繼承自Interception,在Initialize方法中將兩個擴展的 InstanceInterceptionStrategy和 TypeInterceptionStrategy——ExtendedInstanceInterceptionStrat egy和ExtendedTypeInterceptionStrategy添加到UnityContainer的 BuildStrategy列表中。在這個擴展的 Inteception——ExtendedInterception中,被用於動態注冊的 Interceptor定義在ExtendedInterception中,默認為 TransparentProxyInteceptor。下面是ExtendedInterception、 ExtendedInstanceInterceptionStrategy和ExtendedTypeInterceptionStrategy 的定義:

ExtendedInterception:

using  Microsoft.Practices.Unity.InterceptionExtension;
using  Microsoft.Practices.Unity.ObjectBuilder;
namespace  Artech.InterceptableUnity
{
  public class  ExtendedInterception : Interception
  {
    public  IInterceptor Interceptor
    { get; internal set; }
    public ExtendedInterception()
    {
       this.Interceptor = new TransparentProxyInterceptor();
     }
    protected override void Initialize()
     {
      this.Context.Strategies.Add(new  ExtendedInstanceInterceptionStrategy(this), UnityBuildStage.Setup);
      this.Context.Strategies.Add(new  ExtendedTypeInterceptionStrategy(this),  UnityBuildStage.PreCreation);
       this.Context.Container.RegisterInstance<InjectionPolicy>(typeof (AttributeDrivenPolicy).AssemblyQualifiedName, new  AttributeDrivenPolicy());
    }
  }
}

ExtendedInstanceInterceptionStrategy:

using  System;
using Microsoft.Practices.ObjectBuilder2;
using  Microsoft.Practices.Unity;
using  Microsoft.Practices.Unity.InterceptionExtension;
namespace  Artech.InterceptableUnity
{
  public class  ExtendedInstanceInterceptionStrategy :  InstanceInterceptionStrategy
  {
    public  ExtendedInterception Interception
    { get; private set;  }
    public ExtendedInstanceInterceptionStrategy (ExtendedInterception interception)
    {
      if  (null == interception)
      {
         throw new ArgumentNullException("interception");
       }
      this.Interception = interception;
     }
    private static IInstanceInterceptionPolicy  FindInterceptorPolicy(IBuilderContext context)
    {
       Type buildKey = BuildKey.GetType(context.BuildKey);
      Type type = BuildKey.GetType (context.OriginalBuildKey);
       IInstanceInterceptionPolicy policy =  context.Policies.Get<IInstanceInterceptionPolicy> (context.BuildKey, false) ??  context.Policies.Get<IInstanceInterceptionPolicy>(buildKey,  false);
      if (policy != null)
      {
        return policy;
      }
       policy = context.Policies.Get<IInstanceInterceptionPolicy> (context.OriginalBuildKey, false) ??  context.Policies.Get<IInstanceInterceptionPolicy>(type,  false);
      return policy;
    }
     public override void PreBuildUp(IBuilderContext context)
     {
      if (BuildKey.GetType(context.BuildKey) ==  typeof(IUnityContainer))
      {
         return;
      }
      IInstanceInterceptionPolicy  policy = FindInterceptorPolicy(context);
      if (null  != policy)
      {
        if  (policy.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey)))
        {
           this.Interception.SetDefaultInterceptorFor(BuildKey.GetType (context.BuildKey), policy.Interceptor);
        }
      }
      else
      {
         if (this.Interception.Interceptor.CanIntercept(BuildKey.GetType (context.BuildKey)) && this.Interception.Interceptor is  IInstanceInterceptor)
        {
           this.Interception.SetDefaultInterceptorFor(BuildKey.GetType (context.BuildKey), (IInstanceInterceptor) this.Interception.Interceptor);
        }
       }
      base.PreBuildUp(context);
    }
   }
}

ExtendedTypeInterceptionStrategy:

using System;
using  Microsoft.Practices.ObjectBuilder2;
using  Microsoft.Practices.Unity.InterceptionExtension;
namespace  Artech.InterceptableUnity
{
  public class  ExtendedTypeInterceptionStrategy : TypeInterceptionStrategy
   {
    public ExtendedInterception Interception
     { get; private set; }
    public  ExtendedTypeInterceptionStrategy(ExtendedInterception interception)
    {
      if (null == interception)
       {
        throw new ArgumentNullException ("interception");
      }
       this.Interception = interception;
    }
    private  static ITypeInterceptionPolicy GetInterceptionPolicy (IBuilderContext context)
    {
       ITypeInterceptionPolicy policy =  context.Policies.Get<ITypeInterceptionPolicy>(context.BuildKey,  false);
      if (policy == null)
      {
        policy =  context.Policies.Get<ITypeInterceptionPolicy>(BuildKey.GetType (context.BuildKey), false);
      }
      return  policy;
    }
    public override void  PreBuildUp(IBuilderContext context)
    {
       var policy = GetInterceptionPolicy(context);
      if  (null != policy)
      {
        if  (policy.Interceptor.CanIntercept(BuildKey.GetType(context.BuildKey)))
        {
           this.Interception.SetInterceptorFor(BuildKey.GetType(context.BuildKey),  policy.Interceptor);
        }
      }
      else
      {
        if  (this.Interception.Interceptor.CanIntercept(BuildKey.GetType (context.BuildKey)) && this.Interception.Interceptor is  ITypeInterceptor)
        {
           this.Interception.SetDefaultInterceptorFor(BuildKey.GetType (context.BuildKey), (ITypeInterceptor) this.Interception.Interceptor);
        }
       }
      base.PreBuildUp(context);
    }
   }
}

那麼使用的時候,動態注冊Interceptor的操作將不再需 要,如下面代碼片斷所示:

using System;
using  System.Threading;
using  Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;
using Microsoft.Practices.Unity;
using  Microsoft.Practices.Unity.InterceptionExtension;
namespace  Artech.InterceptableUnity
{
  class Program
  {
    static void Main(string[] args)
    {
       IUnityContainer container = new UnityContainer();
       container.RegisterType<ISyncTimeServiceProvision,  SyncTimeServiceProvisionService>();
       container.RegisterType<ISyncTimeServiceProvider,  SyncTimeServiceProvider>();
      ExtendedInterception  interception = new ExtendedInterception();
       interception.Interceptor = new TransparentProxyInterceptor();
      container.AddExtension(interception);
      var  syncTimeServiceProvision =  container.Resolve<ISyncTimeServiceProvision>();
       for (int i = 0; i < 5; i++)
      {
         Console.WriteLine(syncTimeServiceProvision.GetCurrentTime ());
        Thread.Sleep(1000);
      }
    }
 }

三、通過配置的方式應用 ExtendedInterception

為了通過配置的方式應用ExtendedInterception, 我們需要為之定義相應的配置類型,一個繼承自 Microsoft.Practices.Unity.Configuration.UnityContainerExtensionConfigur ationElement得類型。為此,我定義了下面一個ExtendedInterceptionElement類 型,配置屬性為默認的Inteceptor的類型。

using System;
using System.Configuration;
using  Microsoft.Practices.Unity;
using  Microsoft.Practices.Unity.Configuration;
using  Microsoft.Practices.Unity.InterceptionExtension;
namespace  Artech.InterceptableUnity
{
  public class  ExtendedInterceptionElement :  UnityContainerExtensionConfigurationElement
  {
     [ConfigurationProperty("interceptor", IsRequired = false,  DefaultValue = "")]
    public string  Interceptor
    {
      get
      {
        return (string)this["interceptor"];
      }
    }
    public override void  Configure(IUnityContainer container)
    {
       base.Configure(container);
      ExtendedInterception  interception = new ExtendedInterception();
      if (! string.IsNullOrEmpty(this.Interceptor))
      {
         var type = System.Type.GetType(this.Interceptor);
         if (null == type)
        {
           throw new ConfigurationErrorsException(string.Format ("The {0} is not a valid Interceptor.",  this.Interceptor));
        }
        if (! typeof(IInterceptor).IsAssignableFrom(type))
        {
          throw new ConfigurationErrorsException (string.Format("The {0} is not a valid Interceptor.",  this.Interceptor));
        }
         interception.Interceptor = (IInterceptor)Activator.CreateInstance (type);
      }
      container.AddExtension (interception);
    }
  }
}

那麼對於 上面的例子,我麼可以將Type Mapping和ExtendedInterception擴展定義在如下 一個配置文件中:

<?xml version="1.0"  encoding="utf-8" ?>
<configuration>
  <configSections>
  <section name="unity"  type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSe ction,Microsoft.Practices.Unity.Configuration, Version=1.2.0.0,
         Culture=neutral,  PublicKeyToken=31bf3856ad364e35" />
  </configSections>
 <unity>
   <typeAliases>
   <typeAlias  alias="ISyncTimeServiceProvision"  type="Artech.InterceptableUnity.ISyncTimeServiceProvision,Artech.I nterceptableUnity" />
   <typeAlias  alias="SyncTimeServiceProvisionService"  type="Artech.InterceptableUnity.SyncTimeServiceProvisionService,Ar tech.InterceptableUnity" />
   <typeAlias  alias="ISyncTimeProvider"  type="Artech.InterceptableUnity.ISyncTimeProvider,Artech.Intercept ableUnity" />
   <typeAlias  alias="SyncTimeProvider"  type="Artech.InterceptableUnity.SyncTimeProvider,Artech.Intercepta bleUnity" />
  </typeAliases>
  
   <containers>
   <container>
     <types>
     <type  type="ISyncTimeServiceProvision"  mapTo="SyncTimeServiceProvisionService" />
      <type type="ISyncTimeProvider"  mapTo="SyncTimeProvider" />
     </types>
     <extensionConfig>
      <add name="extendedInterception"  type="Artech.InterceptableUnity.ExtendedInterceptionElement,Artech .InterceptableUnity"  interceptor="Microsoft.Practices.Unity.InterceptionExtension.Trans parentProxyInterceptor,Microsoft.Practices.Unity.Interception,  Version=1.2.0.0, Culture=neutral,  PublicKeyToken=31bf3856ad364e35"/>
     </extensionConfig>
   </container>
   </containers>
 </unity>
</configuration>

那麼我們的代碼將會變得異常簡潔:

using System;
using System.Threading;
using  Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers;
using Microsoft.Practices.Unity;
using  Microsoft.Practices.Unity.InterceptionExtension;
using  Microsoft.Practices.Unity.Configuration;
using  System.Configuration;
namespace Artech.InterceptableUnity
{
  class Program
  {
    static void Main (string[] args)
    {
      IUnityContainer  container = new UnityContainer();
       UnityConfigurationSection configuration =  (UnityConfigurationSection)ConfigurationManager.GetSection ("unity") ;
       configuration.Containers.Default.Configure(container);
       var syncTimeServiceProvision =  container.Resolve<ISyncTimeServiceProvision>();
       for (int i = 0; i < 5; i++)
      {
         Console.WriteLine(syncTimeServiceProvision.GetCurrentTime ());
        Thread.Sleep(1000);
      }
    }
  }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved