通过“四大行为”对WCF的扩展[实例篇]

为了让读者对如何利用相应的行为对WCF进行扩展有个深刻的认识,在这里我提供一个简单的实例演示。本实例模拟的场景是这样的:我们创建一个支持多语言的资源服务,该服务旨在为调用者提供基于某种语言的文本型资源。但是,我们不希望客户端在每次调用服务的时候都显式地制定具体的语言,而是根据客户端服务调用线程表示语言文化的上下文来自动识别所需的语言。[源代码从这里下载]

要让资源服务具有识别语言文化的能够,我们必须将客户端服务调用线程当前的语言文化信息(具体来说就是Thread的两个属性:CurrentUICulture和CurrentCulture)自动传递到服务端。我们具体的实现原理是这样的:我们将客户端服务调用线程的CurrentUICulture和CurrentCulture的语言文化代码保存在出栈消息的SOAP报头中,并为它们起一个预定义的名称和命名空间;在服务操作在服务端执行之前,我们根据这个预定义SOAP报头名称和命名空间将这两个语言文化代码从入栈消息中获取出来,创建相应的CultureInfo对象并作为服务操作执行线程的CurrentUICulture和CurrentCulture。那么服务操作在执行的时候,只需要根据当前线程的语言文化上下文提供相应资源就可以了。接下来,我们就来一步一步地实现这样一个简单的扩展。

目录
步骤一、创建自定义CallContextInitializer:CultureReceiver
步骤二、自定义ClientMessageInspector:CultureSender
步骤三、创建行为:CulturePropagationBehaviorAttribute
步骤四、为CulturePropagationBehaviorAttribute定义配置元素
步骤五、创建实例应用检验语言文化的自动传播

步骤一、创建自定义CallContextInitializer:CultureReceiver

所谓客户端当前语言文化信息的传递,无外乎是客户端将当前线程的CurrentUICulture和CurrentCulture放到出栈消息中;而服务端将其从入栈消息中取出,并对当前线程的CurrentUICulture和CurrentCulture进行相应的设置。我们先来实现在服务端用于进行语言文化信息获取的组件,我将其命名为CultureReceiver。

由于CultureReceiver在从入栈消息中获取表示客户端线程的CurrentUICulture和CurrentCulture信息的时,需要预先知道相应报头的名称和命名空间(命名空间仅仅用于SOAP报头),为此我们将这些定义成如下一个名称为CultureMessageHeaderInfo的类。属性CurrentCultureName,CurrentUICultureName和Namespace分别表示代码客户端线程CurrentCulture和CurrentUICulture的报头名称。

   1: namespace Artech.WcfExtensions.CulturePropagation
   2: {
   3:     internal class CultureMessageHeaderInfo
   4:     {
   5:         public string     Namespace{ get; set; }
   6:         public string     CurrentCultureName{ get; set; }
   7:         public string     CurrentUICultureName { get; set; }
   8:     }
   9: }

而我们进行语言文化报头接收以及对服务操作执行线程当前语言文化的设置的CultureReceiver组件被定义成一个实现了接口ICallContextInitializer的CallContextInitializer对象。在介绍WCF服务端运行时框架的时候,我们已经对CallContextInitializer进行了说明。我们说CallContextInitializer的两个方法BeforeInvoke和AfterInvoke方法分别在操作方法执行前后被调用。

而我们恰好可以通过实现BeforeInvoke方法将存放在入栈消息报头的表示客户端线程CurrentCulture和CurrentUICulture的内容取出,并以此创建相应的CultureInfo作为当前线程的CurrentCulture和CurrentUICulture。由于WCF服务端采用线程池的机制处理客户端请求,线程会被重用,所以我们有必要在操作方法执行之后将当前线程的语言文化设置恢复到之前的状态,而这恰好可以实现在AfterInvoke方法中。下面的代码片断表示CultureReceiver的全部定义。

   1: using System.Globalization;
   2: using System.ServiceModel;
   3: using System.ServiceModel.Channels;
   4: using System.ServiceModel.Dispatcher;
   5: using System.Threading;
   6: namespace Artech.WcfExtensions.CulturePropagation
   7: {
   8:     internal class CultureReceiver : ICallContextInitializer
   9:     {
  10:         public CultureMessageHeaderInfo messageHeaderInfo;
  11:         public CultureReceiver(CultureMessageHeaderInfo messageHeaderInfo)
  12:         {
  13:             this.messageHeaderInfo = messageHeaderInfo;
  14:         }
  15:  
  16:         public void AfterInvoke(object correlationState)
  17:         {
  18:             CultureInfo[] cultureInfos = correlationState as CultureInfo[];
  19:             if (null != cultureInfos)
  20:             {
  21:                 Thread.CurrentThread.CurrentCulture = cultureInfos[0];
  22:                 Thread.CurrentThread.CurrentUICulture = cultureInfos[1];
  23:             }
  24:         }
  25:  
  26:         public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
  27:         {
  28:             CultureInfo[] originalCulture = new CultureInfo[] { CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture };
  29:             CultureInfo currentCulture = null;
  30:             CultureInfo currentUICulture = null;
  31:             if (message.Headers.FindHeader(this.messageHeaderInfo.CurrentCultureName, this.messageHeaderInfo.Namespace) > -1)
  32:             {
  33:                 currentCulture = new CultureInfo(message.Headers.GetHeader<string>(this.messageHeaderInfo.CurrentCultureName, 
  34:                     this.messageHeaderInfo.Namespace));
  35:                 Thread.CurrentThread.CurrentCulture = currentCulture;
  36:             }
  37:             if (message.Headers.FindHeader(this.messageHeaderInfo.CurrentUICultureName, this.messageHeaderInfo.Namespace) > -1)
  38:             {
  39:                 currentUICulture = new CultureInfo(message.Headers.GetHeader<string>(this.messageHeaderInfo.CurrentUICultureName, 
  40:                    this.messageHeaderInfo.Namespace));
  41:                 Thread.CurrentThread.CurrentUICulture = currentUICulture;
  42:             }
  43:             return originalCulture;
  44:         }
  45:     }
  46: }

步骤二、自定义ClientMessageInspector:CultureSender

对于客户端,我们通过自定义一个ClientMessageInspector利用“消息检验”机制将代表当前线程CurrentCulture和CurrentUICulture的语言文化代码以SOAP报头的形式植入请求消息中。我们将改自定义的ClientMessageInspector称为CultureSender。如下面的代码所示,上述的关于客户端线程当前语言文化信息的发送实现在BeforeSendRequest方法中。

   1: using System.Globalization;
   2: using System.ServiceModel;
   3: using System.ServiceModel.Channels;
   4: using System.ServiceModel.Dispatcher;
   5: namespace Artech.WcfExtensions.CulturePropagation
   6: {
   7:     internal class CultureSender: IClientMessageInspector
   8:     {
   9:         private CultureMessageHeaderInfo messageHeaderInfo;
  10:  
  11:         public CultureSender(CultureMessageHeaderInfo messageHeaderInfo)
  12:         {
  13:             this.messageHeaderInfo = messageHeaderInfo;
  14:         }
  15:  
  16:         public void AfterReceiveReply(ref Message reply, object correlationState) { }
  17:         public object BeforeSendRequest(ref Message request, IClientChannel channel)
  18:         {
  19:             request.Headers.Add(MessageHeader.CreateHeader(this.messageHeaderInfo.CurrentCultureName, this.messageHeaderInfo.Namespace, CultureInfo.CurrentCulture.Name));
  20:             request.Headers.Add(MessageHeader.CreateHeader(this.messageHeaderInfo.CurrentUICultureName, this.messageHeaderInfo.Namespace, CultureInfo.CurrentUICulture.Name));          
  21:             return null;
  22:         }
  23:     }
  24: }

步骤三、创建行为:CulturePropagationBehaviorAttribute

到目前为止,真正实现语言文化信息从客户端到服务端传播的自定义CallContextInitialier(CultureReceiver)和ClientMessageInspector(CultureSender)都已经创建好了。我们目前需要做的是通过定义相应的行为将这两个自定义组件分别应用到WCF的服务端和客户端运行时框架中去。具体来说,我们需要创建CultureReceiver对象并将其添加到相应DispatchOperation的CallContextInitializer列表之中,创建CultureSender对象并将其添加到ClientRuntime的MessageInspector列表之中。

以下定义的CulturePropagationBehaviorAttribute就是这样一个行为。从下面给出的代码中我们可以看到,CulturePropagationBehaviorAttribute实现了三个行为接口(IServiceBehavior, IEndpointBehavior, IContractBehavior),所以它既是一个服务行为,同时也是一个终结点行为和契约行为。我们自定义的CultureReceiver和CultureSender分别通过ApplyDispatchBehavior和ApplyClientBehavior方法被应用到服务端和客户端运行时框架。

   1: using System;
   2: using System.Collections.ObjectModel;
   3: using System.ServiceModel;
   4: using System.ServiceModel.Channels;
   5: using System.ServiceModel.Description;
   6: using System.ServiceModel.Dispatcher;
   7: namespace Artech.WcfExtensions.CulturePropagation
   8: {
   9:     public class CulturePropagationBehaviorAttribute: Attribute, IServiceBehavior, 
  10:        IEndpointBehavior,  IContractBehavior
  11:     {
  12:         private CultureMessageHeaderInfo messageHeaderInfo;
  13:  
  14:         public const string DefaultNamespace            = "http://www.artech.com/culturepropagation";
  15:         public const string DefaultCurrentCultureName   = "CurrentCultureName";
  16:         public const string DefaultCurrentUICultureName = "CurrentUICultureName";
  17:  
  18:         public string Namespace 
  19:         { 
  20:             get{return messageHeaderInfo.Namespace;} 
  21:             set{messageHeaderInfo.Namespace = value;} 
  22:         }
  23:         public string CurrentCultureName
  24:         {
  25:             get { return messageHeaderInfo.CurrentCultureName; }
  26:             set { messageHeaderInfo.CurrentCultureName = value; }
  27:         }
  28:         public string CurrentUICultureName
  29:         {
  30:             get { return messageHeaderInfo.CurrentUICultureName; }
  31:             set { messageHeaderInfo.CurrentUICultureName = value; }
  32:         }
  33:  
  34:         public CulturePropagationBehaviorAttribute()
  35:         {
  36:             messageHeaderInfo = new CultureMessageHeaderInfo 
  37:                 { 
  38:                     Namespace               = DefaultNamespace, 
  39:                     CurrentCultureName      = DefaultCurrentCultureName, 
  40:                     CurrentUICultureName    = DefaultCurrentUICultureName 
  41:                 };
  42:         }
  43:  
  44:         //IServiceBehavior
  45:         public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, 
  46:              Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {}
  47:         public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  48:         {
  49:             foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
  50:             {
  51:                 foreach (EndpointDispatcher endpoint in channelDispatcher.Endpoints)
  52:                 {
  53:                     foreach (DispatchOperation operation in endpoint.DispatchRuntime.Operations)
  54:                     {
  55:                         operation.CallContextInitializers.Add(new CultureReceiver(messageHeaderInfo));
  56:                     }
  57:                 }
  58:             }
  59:         }
  60:         public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
  61:  
  62:         //IEndpointBehavior
  63:         public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
  64:         public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  65:         {
  66:             clientRuntime.MessageInspectors.Add(new CultureSender(messageHeaderInfo));
  67:         }
  68:         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  69:         {
  70:             foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
  71:             {
  72:                 operation.CallContextInitializers.Add(new CultureReceiver(messageHeaderInfo));
  73:             }
  74:         }
  75:         public void Validate(ServiceEndpoint endpoint) { }
  76:  
  77:         //IContractBehavior
  78:         public void AddBindingParameters(ContractDescription contractDescription, 
  79:           ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
  80:         public void ApplyClientBehavior(ContractDescription contractDescription, 
  81:           ServiceEndpoint endpoint, ClientRuntime clientRuntime)
  82:         {
  83:             clientRuntime.MessageInspectors.Add(new CultureSender(messageHeaderInfo));
  84:         }
  85:         public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, 
  86:           DispatchRuntime dispatchRuntime)
  87:         {
  88:             foreach (DispatchOperation operation in dispatchRuntime.Operations)
  89:             {
  90:                 operation.CallContextInitializers.Add(new CultureReceiver(messageHeaderInfo));
  91:             }
  92:         }
  93:         public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) { }
  94:     }
  95: }

由于作为服务行为和契约行为的CulturePropagationBehaviorAttribute又是一个自定义特性,所以我们可以直接将其应用到服务契约接口(作为契约行为)或者服务类型(作为服务行为)上。在应用该行为特性时,你可以设置CurrentCultureName、CurrentUICultureName和Namespace属性改变封装客户端线程CurrentCulture和CurrentUICulture的SOAP报头的名称和命名空间。如果此三个属性没有经过显式设置,它们具有默认值。

作为契约行为:

   1: [ServiceContract]
   2: [CulturePropagationBehavior(CurrentCultureName = "culture", CurrentUICultureName = "uiCulture")]
   3: public interface IResourceService
   4: {
   5:     [OperationContract]
   6:     string GetString(string key);
   7: }

作为服务行为:

   1: [CulturePropagationBehavior(CurrentCultureName = "culture", CurrentUICultureName = "uiCulture")]
   2: public class ResourceService : IResourceService
   3: {
   4:     public string GetString(string key)
   5:     {
   6:         //省略实现
   7:     }
   8: }

步骤四、为CulturePropagationBehaviorAttribute定义配置元素

除了通过编程的方式应用行为之外,对于WCF四种类型的行为,契约行为和操作行为只能通过以自定义特性的方式以声明的方式分别应用到服务契约接口(或者类)和操作契约方法或者操作实现方法上。终结点行为只能通过配置的方式应用到对应的终结点。而服务行为,则可以同时采用声明和配置的方式应用到目标服务上面。

由于我们定义的CulturePropagationBehaviorAttribute既是服务行为,也是终结点行为,为了实现以配置的方式来使用该行为,我们需要为之创建一个配置元素的类。作为服务行为或者终结点行为的配置元素类均继承自抽象类BehaviorExtensionElement

在这里我们创建了如下一个CulturePropagationBehaviorElement类来定义CulturePropagationBehaviorAttribute的配置元素。在BehaviorExtensionElement中,我们定义了三个可选(IsRequired
=
false)的配置属性CurrentCultureName、CurrentUICultureName和Namespace分别代表用于封装客户端线程CurrentCulture和CurrentUICulture的SOAP报头名称和命名空间。

   1: using System;
   2: using System.Configuration;
   3: using System.ServiceModel.Configuration;
   4: namespace Artech.WcfExtensions.CulturePropagation
   5: {
   6:     public class CulturePropagationBehaviorElement: BehaviorExtensionElement
   7:     {
   8:         [ConfigurationProperty("namespace",IsRequired =false,             
   9:             DefaultValue = CulturePropagationBehaviorAttribute.DefaultNamespace)]
  10:         public string Namespace
  11:         {
  12:             get { return (string)this["namespace"]; }
  13:             set { this["namespace"] = value; }
  14:         }
  15:         [ConfigurationProperty("currentCultureName", IsRequired = false,             
  16:             DefaultValue = CulturePropagationBehaviorAttribute.DefaultCurrentCultureName)]
  17:         public string CurrentCultureName
  18:         {
  19:             get { return (string)this["currentCultureName"]; }
  20:             set { this["currentCultureName"] = value; }
  21:         }
  22:         [ConfigurationProperty("currentUICultureName", IsRequired = false,             
  23:             DefaultValue = CulturePropagationBehaviorAttribute.DefaultCurrentUICultureName)]
  24:         public string CurrentUICultureName
  25:         {
  26:             get { return (string)this["currentUICultureName"]; }
  27:             set { this["currentUICultureName"] = value; }
  28:         }
  29:         public override Type BehaviorType
  30:         {
  31:             get { return typeof(CulturePropagationBehaviorAttribute); }
  32:         }
  33:         protected override object CreateBehavior()
  34:         {
  35:             return new CulturePropagationBehaviorAttribute
  36:             {
  37:                 Namespace = this.Namespace,
  38:                 CurrentCultureName = this.CurrentCultureName,
  39:                 CurrentUICultureName = this.CurrentUICultureName
  40:             };
  41:         }
  42:     }
  43: }

下面的XML片断反映了如何将CulturePropagationBehaviorAttribute作为服务行为以配置的方式应用到目标服务上。首先,我们需要将基于服务行为的配置元素类型以行为或者的形式定义在<extensions>/<behaviorExtensions>结点下,并给它一个名称(在这里将我们定义的扩展起名为culturePropagation)。在配置服务行为的时候,我们只需要在行为配置节点中添加以行为扩展名为元素名的XML结点,并对定义在配置元素类型中的配置属性进行相应的设置即可。在本例中,我们定义了一个名称为defaultSvcBehavior的服务行为。该行为结点的字结点<culturePropagation>代表我们自定义的CulturePropagationBehaviorAttribute行为。在<culturePropagation>结点下,我们对namespace、currentCultureName和currentUICultureName作了显式设置(由于三个配置属性可选的,如果没有对它们进行显式设置,它们将会具有一个默认值)。最终这个名称为defaultSvcBehavior的服务行为被应用到了ResourceService服务上(behaviorConfiguration="defaultSvcBehavior")。

   1: <configuration>
   2:   <system.serviceModel>
   3:     <services>
   4:       <service behaviorConfiguration="defaultSvcBehavior" 
   5:                name="Artech.WcfServices.Servicies.ResourceService">
   6:         <endpoint  address = “http://127.0.0.1:3721/resourceservice”
   7:                    binding="ws2007HttpBinding"  
   8:                    contract="Artech.WcfServices.Contracts.IResourceService" />
   9:       </service>
  10:     </services>
  11:     <behaviors>
  12:       <serviceBehaviors>
  13:         <behavior name="defaultSvcBehavior">
  14:           <culturePropagation 
  15:        namespace="http://www.artech.com/" 
  16:        currentCultureName="cultureName" 
  17:        currentUICultureName="uiCultureName"/>
  18:         </behavior>
  19:       </serviceBehaviors>
  20:     </behaviors>
  21:     <extensions>
  22:       <behaviorExtensions>
  23:         <add name="culturePropagation" 
  24:              type="Artech.WcfExtensions.CulturePropagation.CulturePropagationBehaviorElement, Artech.WcfExtensions.Lib" />
  25:       </behaviorExtensions>
  26:     </extensions>
  27:   </system.serviceModel>
  28: </configuration>

如果你需要将CulturePropagationBehaviorAttribute以终结点行为的方式应用到服务的某个终结点上,配置方式与此类似。首先,你同样需要将代表行为配置元素的类型名称定义成行为扩展。然后将代表该行为配置的XML结点添加到终结点配置节点即可。在下面这段配置中,ResourceService服务具有唯一的终结点,该终结点应用了一个名称为defaultEndpointBehavior的终结点行为。而该种节点行为包含了CulturePropagationBehaviorAttribute的相关配置。

   1: <configuration>
   2:   <system.serviceModel>
   3:     <services>
   4:       <service name="Artech.WcfServices.Servicies.ResourceService">
   5:         <endpoint address     = "http://127.0.0.1:3721/resourceservice"
   6:                   binding     = "ws2007HttpBinding"
   7:                   contract    ="Artech.WcfServices.Contracts.IResourceService" 
   8:                   behaviorConfiguration="defaultEndpointBehavior"/>
   9:       </service>
  10:     </services>
  11:     <behaviors>
  12:       <endpointBehaviors>
  13:         <behavior name="defaultEndpointBehavior">
  14:           <culturePropagation
  15:            Namespace        ="http://www.artech.com/"
  16:            currentCultureName    ="cultureName"
  17:            currentUICultureName="uiCultureName"/>
  18:         </behavior>
  19:       </endpointBehaviors>
  20:     </behaviors>
  21:     <extensions>
  22:       <behaviorExtensions>
  23:         <add name="culturePropagation" type="Artech.WcfExtensions.CulturePropagation.CulturePropagationBehaviorElement, Artech.WcfExtensions.Lib" />
  24:       </behaviorExtensions>
  25:     </extensions>
  26:   </system.serviceModel>
  27: </configuration>

步骤五、创建实例应用检验语言文化的自动传播

到目前为止,关于实现语言文化从客户端自动传播到服务端的所有扩展实现均已完成。为了检验我们自定义的行为CulturePropagationBehaviorAttribute是否真的能够实现这个目标,我们需要通过建立一个简单的WCF应用程序来检验。

我们采用之前介绍过的文本型资源提供服务,为此我们创建了如下一个简单的代表服务契约接口IResourceService,该服务契约具有一个唯一的操作契约方法GetString用于获取基于给定的键值得到的对应的文本型资源的内容。需要注意的是,我们定义的CulturePropagationBehaviorAttribute以契约行为的形式应用到IResourceService接口之上。

   1: using System.ServiceModel;
   2: using Artech.WcfExtensions.CulturePropagation;
   3: namespace Artech.WcfServices.Contracts
   4: {
   5:     [ServiceContract(Namespace = "http://www.artech.com/")]
   6:     [CulturePropagationBehavior]
   7:     public interface IResourceService
   8:     {
   9:         [OperationContract]
  10:         string GetString(string key);
  11:     }
  12: }

为了提供对于多语言的支持,我们将资源文本的内容定义在资源文件中。为此我们在定义服务类型的项目中添加了如下图所示的两个资源文件。其中Resources.resx代表语言文化中性的资源文件,而Resources.zh-CN.resx则代表基于中国(大陆)简体中文的资源文件。两个资源文件定义了英文和中文作为内容的两个文本资源条目HappyNewYear和MerryChristmas。

在默认的情况下,添加语言文化中性的资源文件会自动生成方便访问资源条目的代码。在服务类型的GetString方法中,我就直接使用定义在自动生成的Resources类的静态属性ResourceManager(相应类型为System.Resources.ResourceManager)来获取给定键值的相应文本资源的内容。

   1: using Artech.WcfServices.Contracts;
   2: using Artech.WcfServices.Servicies.Properties;
   3: namespace Artech.WcfServices.Servicies
   4: {
   5:     public class ResourceService : IResourceService
   6:     {
   7:         public string GetString(string key)
   8:         {
   9:             return Resources.ResourceManager.GetString(key);
  10:         }
  11:     }
  12: }

然后,服务ResourceService以控制台应用作为宿主进行简单的自我寄宿。下面是服务端配置和客户端配置,由于我们自定义的CulturePropagationBehaviorAttribute是以声明的方式作为契约行为应用到契约接口上,所以配置中并不包含相关的内容。

服务寄宿端配置:

   1: <configuration>
   2:   <system.serviceModel>
   3:     <services>
   4:       <service name="Artech.WcfServices.Servicies.ResourceService">
   5:         <endpoint  address = "http://127.0.0.1:3721/resourceservice"
   6:                    binding ="ws2007HttpBinding"
   7:                    contract ="Artech.WcfServices.Contracts.IResourceService"/>
   8:       </service>
   9:     </services>
  10:   </system.serviceModel>
  11: </configuration>

客户端配置:

   1: <configuration>
   2:   <system.serviceModel>
   3:     <client>
   4:       <endpoint name ="resourceservice"
   5:                 address = "http://127.0.0.1:3721/resourceservice"
   6:                 binding ="ws2007HttpBinding"
   7:                 contract ="Artech.WcfServices.Contracts.IResourceService"/>
   8:     </client>
   9:   </system.serviceModel>
  10: </configuration>

客户端同样是一个控制台应用,下面是进行服务调用的代码。从中我们可以看到,我们一共进行了四次针对GetString操作的服务调用,在调用之前我们对当前线程的CurrentUICulture(它决定了语言的种类和对资源文件的选择)。前面两次和后面两次是在CurrentUICulture为en-US和zh-CN情况下进行调用的。从输出结果我们可以清晰地看到:客户端得到的资源文本的语言正好是和当前线程的CurrentUICulture一致的,而这正是应用在契约接口上CulturePropagationBehaviorAttribute特性所致。

   1: using System;
   2: using System.ServiceModel;
   3: using System.Threading;
   4: using Artech.WcfServices.Contracts;
   5: namespace Client
   6: {
   7:     class Program
   8:     {
   9:         static void Main(string[] args)
  10:         {
  11:             using(ChannelFactory<IResourceService> channelFactory = new ChannelFactory<IResourceService>("resourceservice"))
  12:             {
  13:                 IResourceService proxy = channelFactory.CreateChannel();
  14:                 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
  15:                 Console.WriteLine(proxy.GetString("HappyNewYear"));
  16:                 Console.WriteLine(proxy.GetString("MerryChristmas")+"\n");
  17:  
  18:                 Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN");
  19:                 Console.WriteLine(proxy.GetString("HappyNewYear"));
  20:                 Console.WriteLine(proxy.GetString("MerryChristmas"));
  21:             }
  22:             Console.Read();
  23:         }
  24:     }
  25: } 

输出结果:

   1: Happy New Year!
   2: Merry Christmas!
   3:  
   4: 新年快乐!
   5: 圣诞快乐!

通过“四大行为”对WCF的扩展[原理篇]

通过“四大行为”对WCF的扩展[实例篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-31 11:25:17

通过“四大行为”对WCF的扩展[实例篇]的相关文章

通过自定义ServiceHost实现对WCF的扩展[实例篇]

在<原理篇>中我们谈到了通过自定义ServiceHost对WCF进行扩展的本质,以及在IIS/WAS寄宿情况下ServiceHostFactory的作用.接下来通过一个具体的例子来演示如何通过WCF扩展实现以Unity为代表的IoC框架的集成,以及应用该扩展的ServiceHost和ServiceHostFactory如何定义.[源代码从这里下载] 目录 一.IoC/DI简介 步骤一.自定义InstanceProvider:UnityInstanceProvider 步骤二.创建服务行为:Un

通过“四大行为”对WCF的扩展[原理篇]

整个WCF框架由两个基本的层次构成,即服务模型层和信道层.对信道层的扩展主要通过针对绑定的扩展实现,具体来说就是自定义绑定元素,以及相关的信道管理器(信道监听器和信道工厂).信道来改变对消息的处理和传输方式. 而对于服务模式型层的扩展则主要体现服务端和客户端运行时框架的定制,进而让WCF按照我们希望的方式进行运作.由于整个运行时框架由一系列的可扩展组件构成,并且大部分运行时属性也可以改写,所以针对服务模型层的扩展具体体现在:根据具体的需要定义相应的组件,并以某种情形将这些自定义的组件应用到运行时

Linux下编译安装php libevent扩展实例

 这篇文章主要介绍了Linux下编译安装php libevent扩展实例,本文着重讲解了编译过程中一个错误解决方法,需要的朋友可以参考下     原本想尝试一下PHP编写高性能网络服务,需要安装libevent扩展,没想到让人很费了点脑袋 先下载libevent扩展: http://pecl.php.net/package/libevent 解压后,开始编译 代码如下: $ cd libevent-version $ /usr/local/php/bin/phpize $ ./configure

[WCF权限控制]利用WCF自定义授权模式提供当前Principal[实例篇]

在<原理篇>中我们谈到:如果采用自定义安全主体权限模式,我们可以通过自定义AuthorizationPolicy或者ServiceAuthorizationManager实现对基于当前认证用于相关的安全主体的提供,进而达到授权的目的.为了让大家对此有个更加深刻的认识,在这篇文章中我们会提供一个具体的例子.[源代码从这里下载] 目录: 一.创建自定义AuthorizationPolicy 二.创建自定义ServiceAuthorizationManager 三.通过自定义Authorizatio

[WCF 4.0新特性] 路由服务[实例篇]

在本篇文章中,我们将通过一个具体的实例来演示如何通过路由服务.在这个例子中,我们会创建连个简单的服务HelloServie和GoodbyeService.假设客户端不能直接调用这两个服务,需要使用到路由服务作为两者之间的中介.整个消息路由的场景如下图所示,中间的GreetingService.svc就是代表路由服务,而两个目标服务则通过HelloServie.svc和GoodbyeService.svc表示.路由服务使用的消息筛选器EndpointAddressMessageFilter,即根据

[WCF权限控制]WCF自定义授权体系详解[实例篇]

在<原理篇>中,我们谈到WCF自定义授权体系具有两个核心的组件:AuthorizationPolicy和ServiceAuthorizationManager,已经它们是如何写作最终提供一种基于声明的授权实现.为了让自定义授权有深刻的理解,我们来进行一个简单实例来演示如何通过自定义这两个组件实现"非角色授权策略".[源代码从这里下载] 目录: 一.创建演示程序解决方案 二.自定义AuthorizationPolicy 三.自定义ServiceAuthorizationMan

jQuery EasyUI API 中文帮助文档和扩展实例_jquery

下载地址:jQuery EasyUI API 中文帮助文档 1.validatebox验证和提示框扩展: //弹框 $.extend({ show_alert: function (strTitle, strMsg) { $.messager.alert(strTitle, strMsg); } }); //扩展validatebox,添加验证 $.extend($.fn.validatebox.defaults.rules, { eqPwd: { validator: function (va

《51单片机应用开发范例大全(第3版)》——2.1 基本器件实现端口扩展实例

2.1 基本器件实现端口扩展实例 目前,比较常用的串行口转换并行口的专用芯片有74LS165.CD4014等,并行口转换串行口的专用芯片有74LS164.CD4094等. 2.1.1 [实例20]用74LS165实现串口扩展并行输入口 一些低速的并行设备,如果直接和单片机连接,则浪费了宝贵的端口资源:如果先经过并行转换,然后以串行方式送入数据,则可以节省I/O端口.本设计就是通过74LS165,利用单片机串口,实现8位并行数据的输入. 1.74LS165与单片机接口电路设计74LS165有多种封

如何解决分布式系统中的跨时区问题[实例篇]

关于如何解决分布式系统中的跨时区问题,上一篇详细介绍了解决方案的实现原理,在这一篇中我们通过一个完整的例子来对这个问题进行深入探讨.尽管<原理篇>中介绍了那么多,解决方案的本质就是:在进行服务调用过程中将客户端的时区信息作为上下文传入服务端,并以此作为时间转换的依据.我们首先定一个具体的类型来定义包含时区信息的上下文类型,我们将这个类型起名为ApplicationContext. 一.通过CallContext实现ApplicationContext 在<通过WCF扩展实现Context