快捷搜索:

WCF后续之旅(5): 通过WCF Extension实现Localization

在上一篇文章中, 我列出了WCF一系列的可扩展工具和元素,并简单先容了他们各自的功能、得当的场景和详细办理的问题。从本篇开始我将经由过程一个个详细的例子来先容若何使用这些扩展点对WCF进行扩展,从而办理一些我们在实现的项目开拓中可能呈现的问题。

本日,我们将评论争论若何经由过程WCF extension实现多说话、本地化的功能。我们模拟这样的一个场景:我们现在有一个支持多说话的项目,假设经由过程支持英文(en-US)和简体中文(zh-CN)。我们必要创建一个service为全部系统供给message。对付这个message service,简单起见,我们将基于不合的culture的message存储于不合的Resource文件中,客户端经由过程造访service来获取基于它自己本地culture的message。比如,假如某一个客户端当前的 culture是en-US,那么会获得英文的message,假如是zh-CN将会获得简体中文的message。

我们很多人会说,在获取message的时刻将client端本地的culture作为API的参数通报到service端,service再根据响应的culture从对应的resource文件中获取message不就可以了吗?这样做不是弗成以,然则不过优雅。从营业逻辑和非营业逻辑的分离来讲是不是一个好的办理规划,由于从某种意义上讲,culture信息是营业无关的,不得当作为API的一部分,API应该只和详细的营业逻辑相关联。

本日给出的办理要领基于这样的实现道理:在Client端,当调用我们的message service的时刻,当前culture被自动放到message header里传到service端;在service端,该culture 信息自动地被掏出,并将service端确当火线程的UI culture设置成该值,那么service只必要根据当火线程的culture去取message就可以了。此外斟酌到我们改变线程culture可能带来的弗成预知的影响,在措施履行完毕将culture重置。

在这里我们先来实现service真个功能:若何从message header中掏出culture,并设置当火线程culture。至于Client真个实现,我们将在另一个场景中进行零丁先容。

若何看过前一篇文章的同伙,大概会记得,在列出的8大年夜dispatching system可扩展工具中,有一个工具很得当我们本日的多说话的场景:CallContextInitializer。顾名思义,CallContext表示基于当火线程的关于Call stack的高低文信息,这样的信息本寄放在TLS(Thread Local Storage)中。CallContextInitializer便是用于去初始化这些context的。实际上,除了call context的初始化事情之外,CallContextInitializer还可以用于call context的清理事情。

1、Message Service

在正式先容CallContextInitializer之前,我们闲来先容一下我们的message service。对付message service的模拟,我们仍旧采纳我们传统的4层布局:Contract、Service、Hosting和Client。

对付Contract,仅仅是下面一个简单的interface:

namespace Artech.Messages.Contract

{

[ServiceContract]

public interface IMessage

{

[OperationContract]

string GetMessage();

}

}在service layer,我经由过程Project property窗口定义了一个默认的Resources.Resources.resx;该resource文件会被保存在Properties目录中;再添加一个新的Resource文件:Resources.zh-CN.resx,并把它拖到Properties目录中。在这两个Resource中定义相同的resource item:

Service的代码很简单,仅仅因此强类型的要领获取该resource item而已:

namespace Artech.Messages.Service

{

public class MessageService:IMessage

{

IMessage Members#region IMessage Members

public string GetMessage()

{

return Resources.HelloWorld;

}

#endregion

}

}

下面是Hosting的Code和configuraion:

namespace Artech.Messages.Hosting

{

public class Program

{

public static void Main()

{

using (ServiceHost host = new ServiceHost(typeof(MessageService)))

{

host.Opened += delegate

{

Console.WriteLine("Message service has been started up!");

};

host.Open();

Console.Read();

}

}

}

}

这是Client真个Configuration:

以及Client真个Code:

namespace Artech.Messages.Client

{

class Program

{

private const string CultureInfoHeadLocalName = "__CultureInfo";

private const string CultyreInfoHeaderNamespace = "urn:artech.com";

static void Main(string[] args)

{

using (ChannelFactory channelFactory = new ChannelFactory("messageservice"))

{

IMessage proxy = channelFactory.CreateChannel();

using (OperationContextScope contextScope = new OperationContextScope(proxy as IContextChannel))

{

MessageHeader header = new MessageHeader(Thread.CurrentThread.CurrentUICulture);

OperationContext.Current.OutgoingMessageHeaders.Add(header.GetUntypedHeader(CultureInfoHeadLocalName,CultyreInfoHeaderNamespace));

Console.WriteLine("The UI culture of current thread is {0}", Thread.CurrentThread.CurrentUICulture);

Console.WriteLine(proxy.GetMessage());

}

Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN");

using (OperationContextScope contextScope = new OperationContextScope(proxy as IContextChannel))

{

MessageHeader header = new MessageHeader(Thread.CurrentThread.CurrentUICulture);

OperationContext.Current.OutgoingMessageHeaders.Add(header.GetUntypedHeader(CultureInfoHeadLocalName, CultyreInfoHeaderNamespace));

Console.WriteLine("The UI culture of current thread is {0}", Thread.CurrentThread.CurrentUICulture);

Console.WriteLine(proxy.GetMessage());

}

}

Console.Read();

}

}

}

这时刻我们运行法度榜样,将会获得若何的输出:

4、经由过程EndpointBehavior运用CallContextInitializer

我们接着来评论争论另一种运用CallContextInitializer的要领:经由过程EndpointBehavior。为此u,我们创建了我们的EndpointBehavior:CultureSettingBehavior。

namespace Artech.CallContextInitializers

{

public class CultureSettingBehavior: IEndpointBehavior

{

IEndpointBehavior Members#region IEndpointBehavior Members

public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

{}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

{}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

{

foreach (var operation in endpointDispatcher.DispatchRuntime.Operations)

{

operation.CallContextInitializers.Add(new CultureSettingCallContextInitializer());

}

}

public void Validate(ServiceEndpoint endpoint)

{}

#endregion

}

}

那么我们就可以根据设置设置设备摆设摆设文件来利用我们的自定义的EndpointBehavior了:

configuration>

system.serviceModel>

behaviors>

endpointBehaviors>

behavior name="cultureSettingBehavior">

cultureSettingElement />

behavior>

endpointBehaviors>

behaviors>

extensions>

behaviorExtensions>

add name="cultureSettingElement" type="Artech.CallContextInitializers.CultureSettingBehaviorElement, Artech.CallContextInitializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

behaviorExtensions>

extensions>

services>

service name="Artech.Messages.Service.MessageService">

endpoint behaviorConfiguration="cultureSettingBehavior" binding="basicHttpBinding"

contract="Artech.Messages.Contract.IMessage" />

host>

baseAddresses>

add baseAddress="http://127.0.0.1/messageservice" />

baseAddresses>

host>

service>

services>

system.serviceModel>

configuration>

您可能还会对下面的文章感兴趣: