您的位置:首页 > 移动开发

[原创]WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成

2008-07-29 09:16 831 查看
上一篇文章中,我们通过自定义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系列(http://www.cnblogs.com/artech/archive/2008/01/29/1057379.html)。

由于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的结果:
 



 

 

 

WCF后续之旅:
[原创]WCF后续之旅(1): WCF是如何通过Binding进行通信的
[原创]WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel
[原创]WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher
[原创]WCF后续之旅(4):WCF Extension Point 概览
[原创]WCF后续之旅(5): 通过WCF Extension实现Localization
[原创]WCF后续之旅(6): 通过WCF Extension实现Context信息的传递
[原创]WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
[原创]WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
[原创]WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I]
[原创]WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II]
[原创]WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance

我的WCF之旅:
[原创]我的WCF之旅(1):创建一个简单的WCF程序
[原创]我的WCF之旅(2):Endpoint Overview
[原创]我的WCF之旅(3):在WCF中实现双向通信(Bi-directional Communication)
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part I
[原创]我的WCF之旅(4):WCF中的序列化(Serialization)- Part II
[原创]我的WCF之旅(5):Service Contract中的重载(Overloading)
[原创]我的WCF之旅(6):在Winform Application中调用Duplex Service出现TimeoutException的原因和解决方案
[原创]我的WCF之旅(7):面向服务架构(SOA)和面向对象编程(OOP)的结合——如何实现Service Contract的继承
[原创]我的WCF之旅(8):WCF中的Session和Instancing Management
[原创]我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
[原创]我的WCF之旅(10): 如何在WCF进行Exception Handling
[原创]我的WCF之旅(11):再谈WCF的双向通讯-基于Http的双向通讯 V.S. 基于TCP的双向通讯
[原创]我的WCF之旅(12):使用MSMQ进行Reliable Messaging
[原创]我的WCF之旅(13):创建基于MSMQ的Responsive Service
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐