程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF後續之旅(8)

WCF後續之旅(8)

編輯:關於.NET

WCF後續之旅(8):通過WCF Extension 實現與MS Enterprise Library Policy Injection Application Block的集成

在上一篇文章中,我們通過自定義InstanceProvider實現了WCF和微軟Enterprise Library Unity Application Block的集成,今天我們已相同的方式實現WCF與Enterprise Library的另一個Application Block的集成:Policy Injection Application Block (PIAB)。

PIAB,通過Method Interception的機制實現了AOP(Aspect Oriented Programing)。按照PIAB的編程方式,我們將非業務邏輯,比如Caching、Authorization、Transaction Enlist、Auditing、ExceptionHandling扽等等, 定義在一個個的CallHandler,這些CallHandler通過Attribute或者Configuration的方式應用到目標方法上。

由於PIAB特殊的實現機制(PIAB實現原理),我們需要通過PIAB的PolicyInjector來創建新的對象或者包裝現有的目標對象。只有調用這種能夠方式的對象,應用在上面的CallHandler才能被執行。所以WCF和PIAB的核心問題就是如何通過PIAB PolicyInjector來創建新的Service Instance,或者包裝已經生成的service instance。在上面一篇文章中,我們通過Unity Container重新定義了InstanceProvider,我們今天的實現方案也是通過自定義InstanceProvider的方式來實現,不是我們需需要通過PolicyInjector來進行對象的創建。

1、創建基於PolicyInjection的InstanceProvider

下面是我們新的InstanceProvider(PolicyInjectionInstanceProvider )的定義

namespace Artech.WCFExtensions

{
  public class PolicyInjectionInstanceProvider : IInstanceProvider
  {
    private Type _serviceContractType;
    private string _policyInjectorName;

    public PolicyInjectionInstanceProvider(Type serviceContractType, string policyInjectorName)
    {
      this._serviceContractType = serviceContractType;
      this._policyInjectorName = policyInjectorName;
    }

#region IInstanceProvider Members

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
      PolicyInjector policyInjector = null;
      if (string.IsNullOrEmpty(this._policyInjectorName))
      {
        policyInjector = new PolicyInjectorFactory().Create();
      }
      else
      {
        policyInjector = new PolicyInjectorFactory().Create(this._policyInjectorName);
      }

      Type serviceType = instanceContext.Host.Description.ServiceType;
      object serviceInstance = Activator.CreateInstance(serviceType);
      if (!this._serviceContractType.IsInterface && !serviceType.IsMarshalByRef && policyInjector is RemotingPolicyInjector)
      {
        return serviceInstance;
      }

      return policyInjector.Wrap(serviceInstance, this._serviceContractType);
    }

    public object GetInstance(InstanceContext instanceContext)
    {
      return this.GetInstance(instanceContext, null);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
      IDisposable disposable = instance as IDisposable;
      if (disposable != null)
      {
        disposable.Dispose();
      }
    }

    #endregion
  }
}

我們對PolicyInjectionInstanceProvider 的實現進行簡單的說明:在PIAB中真正用於創建對象的是PolicyInjector,雖然PIAB中僅僅定義了一種基於Remoting的RemotingPolicyInjector,但是我們可以根據我們的需要實現一些不同Injection方式,比如IL Injection。所以我們定義了一個字段_policyInjectorName在配置中定位我們需要的PolicyInjector。該字段如果為null或者empty,將使用默認的PolicyInjector。PolicyInjection的獲取通過下面的代碼實現:

PolicyInjector policyInjector = null;
if (string.IsNullOrEmpty(this._policyInjectorName))
{
  policyInjector = new PolicyInjectorFactory().Create();
}
else
{
  policyInjector = new PolicyInjectorFactory().Create(this._policyInjectorName);
}

能夠被RemotingPolicyInjector創建的對象不是滿足下面兩個條件中的一個:

Target type實現一個Interface。

Target Type直接或者間接集成System.MarshalByRefObject.

所以如果不能滿足這個條件,我們直接通過反射創建service instance:

Type serviceType = instanceContext.Host.Description.ServiceType;
object serviceInstance = Activator.CreateInstance(serviceType);
if (!this._serviceContractType.IsInterface && !serviceType.IsMarshalByRef && policyInjector is RemotingPolicyInjector)
{
     return serviceInstance;
}

最後我們通過policyInjector 的Wrap方法對service instance進行封裝並返回:

return policyInjector.Wrap(serviceInstance, this._serviceContractType);

2、為PolicyInjectionInstanceProvider創建Behavior

我們可以通過ContractBehavior或者EndpointBehavior應用我們定義的PolicyInjectionInstanceProvider 。

I、ContractBehavior:PolicyInjectionBehaviorAttribute

namespace Artech.WCFExtensions

{
  public class PolicyInjectionBehaviorAttribute:Attribute, IContractBehavior
  {
    public string PolicyInjectorName
    { get; set; }

    #region IContractBehavior Members

    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {}

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {}

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
      Type serviceContractType = contractDescription.ContractType;
      dispatchRuntime.InstanceProvider = new PolicyInjectionInstanceProvider(serviceContractType, this.PolicyInjectorName);
    }

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {}

    #endregion
  }
}

我們在ApplyDispatchBehavior,通過contractDescription.ContractType獲得service contract type,然後創建我們的PolicyInjectionInstanceProvider, 並將其指定成當前DispatchRuntime 的InstanceProvider 。PolicyInjector通過屬性PolicyInjectorName進行設置。

II、Endpoint Behavior & Behavior Extension: PolicyInjectionBehavior

namespace Artech.WCFExtensions

{
 public class PolicyInjectionBehavior:IEndpointBehavior
  {
    private string _policyInjectorName;

    public PolicyInjectionBehavior(string policyInjectorName)
    {
      this._policyInjectorName = policyInjectorName;
    }

    #region IEndpointBehavior Members

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {}

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {}

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
      Type serviceContractType = endpoint.Contract.ContractType;
      endpointDispatcher.DispatchRuntime.InstanceProvider = new PolicyInjectionInstanceProvider(serviceContractType, this._policyInjectorName);
    }

    public void Validate(ServiceEndpoint endpoint)
    {}

    #endregion
  }
}

當前DispatchRuntime的InstanceProvider 在ApplyDispatchBehavior方法中指定,PolicyInjectorName通過配置文件配置。該配置節通過下面的PolicyInjectionBehaviorElement定義:

namespace Artech.WCFExtensions

{
  public class PolicyInjectionBehaviorElement:BehaviorExtensionElement
  {
    [ConfigurationProperty("policyInjectorName",IsRequired = false, DefaultValue = "")]
    public string PolicyInjectorName
    {
      get
      {
        return this["policyInjectorName"] as string;
      }
      set
      {
        this["policyInjectorName"] = value;
      }
    }

    public override Type BehaviorType
    {
      get { return typeof(PolicyInjectionBehavior); }
    }

    protected override object CreateBehavior()
    {
      return new PolicyInjectionBehavior(this.PolicyInjectorName);
    }
  }
}

3、應用我們的PolicyInjectionBehavior

現在模擬一個WCF的場景來應用我們創建的PolicyInjectionBehavior。為了直觀我們我們創建一個Timeservice,用於返回當前的系統之間,然後我們運用PIAB的CachingCallHandler。那麼我們可以通過返回值是否反映真正的當前時間來判斷Policy Injection是否起作用了。

我們依然采用我們的4層結構程序構架:

I、Artech.TimeService.Contract

namespace Artech.TimeService.Contract

{
  [ServiceContract]
  [PolicyInjectionBehavior]
  public interface ITime
  {
    [OperationContract]
    DateTime GetCurrentTime();
  }
}

我們先試驗ContractBehavior,我們僅僅需要將PolicyInjectionBehaviorAttribute應用到ServiceContract上。

II、Artech.TimeService.Service

namespace Artech.TimeService.Service

{
  public class TimeService:ITime
  {
    #region ITime Members

    [CachingCallHandler]
    public DateTime GetCurrentTime()
    {
      return DateTime.Now;
    }

    #endregion
  }
}

我們在GetCurrentTime方法上應用了CachingCallHandlerAttribute,那麼在第一次執行該方法的時候,方法返回的結果會被緩存,緩存的Key將會是方法和參數值的列表。後續的執行,將會直接從Cache中獲取已經執行過的結果。

III、Artech.TimeService.Hosting

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Artech.TimeService.Service.TimeService">
        <endpoint behaviorConfiguration="" binding="basicHttpBinding"
          contract="Artech.TimeService.Contract.ITime" />
        <host>
          <baseAddresses>
            <add baseAddress="http://127.0.0.1/timeservice" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

namespace Artech.TimeService.Hosting

{
  class Program
  {
    static void Main(string[] args)
    {
      using (ServiceHost host = new ServiceHost(typeof(Artech.TimeService.Service.TimeService)))
      {
        host.Opened += delegate
        {
          Console.WriteLine("Time service has been started up!");
        };
        host.Open();

        Console.Read();
      }
    }
  }
}

IV、Artech.TimeService.Client

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://127.0.0.1/timeservice" binding="basicHttpBinding"
        contract="Artech.TimeService.Contract.ITime" name="timeservice" />
    </client>
  </system.serviceModel>
</configuration>

namespace Artech.TimeService.Client

{
  class Program
  {
    static void Main(string[] args)
    {
      using (ChannelFactory<ITime> channelFactory = new ChannelFactory<ITime>("timeservice"))
      {
        ITime proxy = channelFactory.CreateChannel();

        for (int i = 0; i < 10; i++)
        {
          Console.WriteLine(proxy.GetCurrentTime());
          Thread.Sleep(1000);
        }
      }

      Console.Read();
    }
  }
}

下面是最終輸出的結果:

從返回的時間都是相同的,我們可以確認caching發揮了作用,如何我們將Contract上[PolicyInjectionBehavior]注釋掉。

namespace Artech.TimeService.Contract
{
  [ServiceContract]
  //[PolicyInjectionBehavior]
  public interface ITime
  {
    [OperationContract]
    DateTime GetCurrentTime();
  }
}

我們將會得到這樣的結果:

上面我們演示了ContractBehavior的應用,我們接著來演示EndpointBehavior的應用。我們僅僅需要修改Hosting的cnonfiguration就可以了:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="PolicyInjectionBehavior">
          <PolicyInjectionBehaviorExtension />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="PolicyInjectionBehaviorExtension" type="Artech.WCFExtensions.PolicyInjectionBehaviorElement, Artech.WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <services>
      <service name="Artech.TimeService.Service.TimeService">
        <endpoint behaviorConfiguration="PolicyInjectionBehavior" binding="basicHttpBinding"
          contract="Artech.TimeService.Contract.ITime" />
        <host>
          <baseAddresses>
            <add baseAddress="http://127.0.0.1/timeservice" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

此時運行我們的程序一樣可以得到被返回值被Cache的結果:

本文配套源碼

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