WCF后续之旅(5): 通过WCF Extension实现Localization
在html" target=_blank>上一篇文章中, 我列出了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的清理工作。
一、Message Service
在正式介绍CallContextInitializer之前,我们闲来介绍一下我们的message service。对于message service的模拟,我们仍然采用我们传统的4层结构:Contract、Service、Hosting和Client。对于Contract,仅仅是下面一个简单的inte易做图ce:
1: namespace Artech.Messages.Contract
在service layer,我通过Project property窗口定义了一个默认的Resources.Resources.resx;该resource文件会被保存在Properties目录中;再添加一个新的Resource文件:Resources.zh-CN.resx,并把它拖到Properties目录中。在这两个Resource中定义相同的resource item:
2: {
3: [ServiceContract]
4: public inte易做图ce IMessage
5: {
6: [OperationContract]
7: string GetMessage();
8: }
9: }
Service的代码很简单,仅仅是以强类型的方式获取该resource item而已
:
1: namespace Artech.Messages.Service
2: {
3: public class MessageService:IMessage
4: {
5: public string GetMessage()
6: {
7: return Resources.HelloWorld;
8: }
9: }
10: }下面是Hosting的Code和configuraion:
1: namespace Artech.Messages.Hosting
2: {
3: public class Program
4: {
5: public static void Main()
6: {
7: using (ServiceHost host = new ServiceHost(typeof(MessageService)))
8: {
9: host.Opened += delegate
10: {
11: Console.WriteLine("Message service has been started up!");
12: };
13:
14: host.Open();
15:
16: Console.Read();
17: }
18: }
19: }
20: }
1: <configuration>
2: <system.serviceModel>
3: <services>
4: <service name="Artech.Messages.Service.MessageService">
5: <endpoint binding="basicHttpBinding" contract="Artech.Messages.Contract.IMessage" />
6: <host>
7: <baseAddresses>
8: <add baseAddress="http://127.0.0.1/messageservice" />
9:  
补充:综合编程 , 其他综合 ,