【开篇】
一直使用.net开发一些基本的web应用,现在需要往移动开发上扩展,自然需要学习微软重金打造的SOA框架WCF,所以打算将自己的学习笔记做成一个系列,方便日后查看,另外方便接受大家的意见、建议,以助于自身能力提升。
【资源】
在开始学习之前,先备齐货:
- WCF维基百科: http://zh.wikipedia.org/wiki/WCF
- MSDN WCF资料:http://msdn.microsoft.com/zh-cn/library/dd456779.aspx
- 书:我用的是网上别人推荐的《WCF全面解析》,刚开始看,不好评价
- ebook: 网上比较容易找到中文版的是《WCF揭密》,不过有点老2008年的,其他的多为英文版,可根据自己需要去找
【SOA特点】
- 服务是自治的:服务可以独立地进行部署及实施版本策略和安全策略;
- 依赖于开放的标准:便于统一
- 支持跨平台
- 鼓励创建可组合的服务:可以用原子服务组合、编排成新的聚合服务;//这是否同前面的“自治”相矛盾?
- 鼓励服务复用
- 强调松耦合:通过“契约”实现客户端对服务的调用,契约匹配即可,不论服务是否改动
【WCF特点】
WCF是Windows平台下各种分布式技术的整合,包含所有以前的分布式通信技术,提供一套统一的API
//下面这段摘自MSDN——begin
-
服务导向
使用 WS 标准的一个结果是,WCF 允许您创建面向服务的应用程序。 面向服务的体系结构 (SOA) 依赖 Web 服务发送和接收数据。 这些服务具有松耦合的常规优点,而不是从一个应用程序到另一个应用程序进行硬编码。 松耦合关系意味着只要符合基本协定,则在任何平台上创建的任何客户端均可连接到所有服务。
-
互操作性
WCF 实现了 Web 服务互操作性的现代行业标准。 有关 支持的标准,请参见 互操作性和集成。
-
多种消息模式
采用多种模式之一交换消息。 最常用的模式是请求/答复模式,其中一个终结点从另一个终结点请求数据, 另一个终结点进行答复。 还有其他模式,比如单向消息,其中只有一个终结点发送消息,而不期望得到答复。 更复杂的模式是双工交换模式,在该模式下,两个终结点建立连接并来回发送数据,类似于即时消息传递程序。 有关 如何实现不同消息交换模式使用 WCF 请参阅 协定。
-
服务元数据
WCF 支持使用行业标准(如 WSDL、XML 架构和 WS-Policy)中指定的格式发布服务元数据。 该元数据可用于自动生成并配置客户端,以便访问 WCF 服务。 可通过 HTTP 和 HTTPS 来发布元数据,也可使用 Web 服务元数据交换标准来发布元数据。 有关更多信息,请参见元数据.
-
数据协定
由于 WCF 是使用 .NET Framework 生成的,因此它也包括代码友好的方法,用于提供希望强制执行的协定。 数据协定就是其中一种通用类型的协定。 实质上,当您使用 Visual C# 或 Visual Basic 对服务进行编码时,处理数据的最简单方法是使用属于数据实体的属性创建表示该数据实体的类。 WCF 包括一个有关这种轻松的方式处理数据的综合系统。 在创建了表示数据的类之后,服务会自动生成使客户端能够符合所设计数据类型的元数据。 有关更多信息,请参见使用数据协定
-
安全性
可对消息进加密以保护隐私,而且可以要求用户对其自身进行身份验证,然后才允许接收消息。 可使用众所周知的标准(如 SSL 或 WS-SecureConversation)实现安全性。 有关更多信息,请参见Windows Communication Foundation 安全性.
-
多种传输和编码方式
可通过多种内置传输协议和编码中的任意一种发送消息。 最常用的协议和编码是使用超文本传输协议 (HTTP) 发送文本编码的 SOAP 消息,以便在万维网上使用。此外,WCF 还允许通过 TCP、命名管道或 MSMQ 发送消息。 这些消息可以编码为文本,也可以使用优化的二进制格式。 使用 MTOM 标准可有效地发送二进制数据。 如果所提供的传输或编码方式都不符合您的需要,您可以创建自己的自定义传输或编码。 有关 传输和所支持的编码 WCF 请参阅 Windows Communication Foundation 中的传输。
-
可靠的排队消息
WCF 支持使用通过 WS-ReliableMessaging 实现的可靠会话并使用 MSMQ 进行可靠的消息交换。 有关 可靠和队列中的消息的支持,在 WCF 请参阅 队列和可靠会话。
-
持久性消息
持久性消息决不会由于通信中断而丢失。 持久性消息模式的消息会始终保存到数据库中。 如果发生中断,数据库将允许您在恢复连接后恢复消息交换。 此外,也可以使用 Windows Workflow Foundation (WF) 来创建持久性消息。 有关更多信息,请参见工作流服务.
-
事务
WCF 还支持使用三个事务模型之一的事务:WS-AtomicTtransactions、System.Transactions 命名空间中的 API 以及 Microsoft 分布式事务协调器。 有关 传输支持 WCF 请参见 事务。
-
AJAX 和 REST 支持
REST 只是 Web 2.0 技术演进的一个示例。 WCF 可以配置为"平淡"进程不换行 SOAP 信封中的 XML 数据。 WCF 此外可以进行扩展以支持特定的XML格式,如 ATOM(流行的 RSS 标准),甚至非 XML 格式,例如,JavaScript 对象表示法 (JSON)。
-
可扩展性
WCF 体系结构具有大量扩展点。 如果需要额外功能,它还提供许多入口点,允许您自定义服务的行为。 有关 可用的扩展点请参见 扩展 WCF。
//上面这段摘自MSDN——end
看了上面的介绍,给人的感觉就是WCF和牛X,学会它,就能掌握.net复合型应用的精髓。包含的多,自然就难了,咱先从简单的开始。
【入门实例】
可以参考msdn的入门示例:http://msdn.microsoft.com/zh-cn/library/ms751519.aspx
我这参考《WCF全面解析》中的第一个示例:
- 步骤一:构建项目(解决方案)
如上图,新建一个解决方案,在其中新建4个项目
1)Service.Interface:定义服务契约(Service Contract),添加引用:System.ServiceModel。项目类型:C#类库
2)Service:定义服务类,实现Service.Interface中的契约接口,添加引用:Service.Interface项目。项目类型:C#类库
3)Hosting:服务器应用程序,添加引用:System.ServiceModel、Service.Interface项目、Service项目。项目类型:C#控制台,解决方案启动项目
4)Client:客户端应用程序,添加引用:System.ServiceModel。项目类型:C#控制台
2.步骤二:创建服务契约Service.Interface(服务接口)
服务接口自然是服务的抽象,在WCF中,将服务接口称为“契约”,建立“契约”的方式和定义接口的一样:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.ServiceModel; 7 8 namespace WCFBegin.Service.Interface 9 { 10 [ServiceContract(Name="CalculatorService", 11 Namespace="http://www.chutianshu.com")] 12 public interface ICalculator 13 { 14 [OperationContract] 15 double Add(double x, double y); 16 [OperationContract] 17 double Subtract(double x, double y); 18 [OperationContract] 19 double Multiply(double x, double y); 20 [OperationContract] 21 double Divide(double x, double y); 22 } 23 }
3.步骤三:创建服务
实现契约(服务接口),和普通类实现接口方法一样
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WCFBegin.Service.Interface; namespace WCFBegin.Service { public class CalculatorService:ICalculator { #region ICalculator 成员 double ICalculator.Add(double x, double y) { return x + y; } double ICalculator.Subtract(double x, double y) { return x - y; } double ICalculator.Multiply(double x, double y) { return x * y; } double ICalculator.Divide(double x, double y) { return x / y; } #endregion } }
4.步骤四:通过自我寄宿的方式寄宿服务(自己编写主机程序发布服务)
当服务契约(接口)和服务类创建好后,要有一种方式把服务发布出来,这里常用有两种方法:
1)通过自我寄宿的方式:
自行创建HOST端,通过自己创建的主机程序发布服务。这里的步骤四就是使用这种方法。
[服务寄宿]:为服务制定一个宿主的过程。
[终结点(Endpoint)]:WCF采用基于Endpoint的通信手段。Endpoint=ABC
-
-
- Adrress(地址):地址决定了服务的位置;
- Binding(绑定):绑定实现通信的细节——网络传输、消息编码,以及其他为实现某种功能(比如传输安全、可靠消息传输、事务等)对消息进行相应处理;
- Contract(契约):对服务操作的抽象,也是对消息交换模式及消息结构的定义。
-
Hosting(自我寄宿主机)的实现方法有三种:
i.)通过代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.ServiceModel; 7 using System.ServiceModel.Description; 8 using WCFBegin.Service.Interface; 9 using WCFBegin.Service; 10 11 namespace WCFBegin.Hosting 12 { 13 class Program 14 { 15 static void Main(string[] args) 16 { 17 using(ServiceHost host=new ServiceHost(typeof(CalculatorService))) 18 { 19 host.AddServiceEndpoint(typeof(ICalculator),new WSHttpBinding(),"http://127.0.0.1:3721/calculatorservice"); 20 if(host.Description.Behaviors.Find<ServiceMetadataBehavior>()==null) 21 { 22 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); 23 behavior.HttpGetEnabled = true; 24 behavior.HttpGetUrl = new Uri("http://127.0.0.1:3721/calculatorservice/metadata"); 25 host.Description.Behaviors.Add(behavior); 26 } 27 host.Opened += delegate 28 { 29 Console.WriteLine("CalculatorService 已经启动,按任意键终止服务!"); 30 }; 31 host.Open(); 32 Console.Read(); 33 } 34 } 35 } 36 }
ii.)在正式WCF开发时,一般采用配置,而不是编程的方式进行Endpoint的添加和服务行为的定义。上面代码中添加Endpoint和定义服务行为的代码可以替换为:
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <system.serviceModel> 4 <behaviors> 5 <serviceBehaviors> 6 <behavior name="metadataBehavior"> 7 <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:3721/calculatorservice/metadata"/> 8 </behavior> 9 </serviceBehaviors> 10 </behaviors> 11 <services> 12 <service name="WCFBegin.Service.CalculatorService" behaviorConfiguration="metadataBehavior"> 13 <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="basicHttpBinding" 14 bindingConfiguration="" contract="WCFBegin.Service.Interface.ICalculator" /> 15 </service> 16 </services> 17 </system.serviceModel> 18 </configuration>
上面的放在Hosting项目的App.config里。
iii.)刚开始,也可以使用WCF服务配置编辑器来配置App.config中的代码
[工具]菜单--[WCF服务配置编辑器],选中Service项目中生成的dll文件,创建服务,根据向导可以自动生成App.config,再将其覆盖Hosting项目中原有的即可
如果使用ii)和iii)中的方式设置了App.config,就可以将i)中的代码缩减为:
static void Main(string[] args) { using(ServiceHost host=new ServiceHost(typeof(CalculatorService))) { host.Opened += delegate { Console.WriteLine("CalculatorService 已经启动,按任意键终止服务!"); }; host.Open(); Console.Read(); } }
完成以上的操作后,就可以编译,如果正常通过,在浏览器中输入http://127.0.0.1:3721/calculatorservice/metadata,就可以看到以WSDL形式表示的服务元数据了。
2)通过IIS寄宿服务:
将服务寄宿在IIS上,通过IIS发布服务,放在步骤六中介绍
5.步骤五:创建客户端程序来调用服务
为Client项目添加服务引用,地址为服务元数据的地址:http://127.0.0.1:3721/calculatorservice/metadata,“转到”后,给个命名空间CalculatorService就可以用了。
*注意:添加服务时,服务必须保持运行状态,即需要打开Hosting.exe
WCF原理复杂,使用简单,刚才的操作会由VS自动生成一系列用于服务调用的代码和配置。(所以有人说.net不适合学习,只适合使用)。
添加服务引用后,用于客户端调用的服务契约接口CalculatorService会被生成出来。
真正被客户端用于服务调用的是一个叫做CalculatorServiceClient的类,派生自System.ServiceModel.ClientBase<CalculatorService>。
CalculatorServiceClient类同样实现了契约接口,并通过调用从基类继承的Channel属性的相应方法实现了4个运算操作方法。
Client代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using WCFBegin.Client.CalculatorServices; 7 8 9 namespace WCFBegin.Client 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 using (CalculatorServiceClient proxy = new CalculatorServiceClient()) 16 { 17 Console.WriteLine("x+y={2} when x={0} and y={1}.",1,2,proxy.Add(1,2)); 18 Console.WriteLine("x-y={2} when x={0} and y={1}.", 1, 2, proxy.Subtract(1, 2)); 19 Console.WriteLine("x*y={2} when x={0} and y={1}.", 1, 2, proxy.Multiply(1, 2)); 20 Console.WriteLine("x/y={2} when x={0} and y={1}.", 1, 2, proxy.Divide(1, 2)); 21 Console.Read(); 22 } 23 } 24 } 25 }
6.步骤六:通过IIS寄宿服务
在步骤三中,我们使用的是自我寄宿,这里介绍IIS寄宿:
1)创建.svc文件
基于IIS的服务寄宿要求相应的WCF服务具有相应的.svc文件,将其部署到IIS站点中即可提供WCF服务。
.svc文件:
直接在Service项目根目录中添加一个文本文件,重命名为CalculatorService.svc,代码为:
<%@ServiceHost Service="WCFBegin.Service.CalculatorService" %>
2)创建Web应用
以Service项目为根目录创建一个Web应用,并添加Web.config文件,添加代码:
1 <configuration> 2 <system.serviceModel> 3 <behaviors> 4 <serviceBehaviors> 5 <behavior name="metadataBehavior"> 6 <serviceMetadata httpGetEnabled="true"/> 7 </behavior> 8 </serviceBehaviors> 9 </behaviors> 10 <services> 11 <service name="WCFBegin.Service.CalculatorService" behaviorConfiguration="metadataBehavior"> 12 <endpoint binding="basicHttpBinding" bindingConfiguration="" contract="WCFBegin.Service.Interface.ICalculator"/> 13 </service> 14 </services> 15 </configuration>
由于服务调用是通过访问服务对应的.svc文件来实现的,.svc文件地址对客户端来说就是服务Endpoint的address,所以将Config中的address都删除。
为Web项目添加引用,可以更改Service项目属性,将编译输出目录设置为\bin,再重新生成一次即可。
这时,就可以通过http://localhost:800/calculatorservice.svc?wsdl(这是我的地址,自己的根据各自IIS配置不同而不同),来得到服务元数据的WSDL文件。
客户端只需要修改Endpoint的地址,从而转向对寄宿在IIS下的CalculatorService的访问:
1 <configuration> 2 <system.serviceModel> 3 <behaviors> 4 <serviceBehaviors> 5 <behavior name="metadataBehavior"> 6 <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:3721/calculatorservice/metadata"/> 7 </behavior> 8 </serviceBehaviors> 9 </behaviors> 10 <services> 11 <service name="WCFBegin.Service.CalculatorService" behaviorConfiguration="metadataBehavior"> 12 <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="basicHttpBinding" 13 bindingConfiguration="" contract="WCFBegin.Service.Interface.ICalculator" /> 14 </service> 15 </services> 16 </system.serviceModel> 17 </configuration>
【总结】
第一篇介绍了WCF的基本概念,和一个简单的WCF实例,可以看出WCF使用起来简单,但是本身结构原理较为复杂。
很多东西都是系统自动生成的,对其运行原理和技术细节还很模糊,需要在下面的学习中进一步了解。