我的WCF之旅1创建一个简单的WCF程序.docx
《我的WCF之旅1创建一个简单的WCF程序.docx》由会员分享,可在线阅读,更多相关《我的WCF之旅1创建一个简单的WCF程序.docx(34页珍藏版)》请在冰豆网上搜索。
我的WCF之旅1创建一个简单的WCF程序
为了使读者对基于WCF的编程模型有一个直观的映像,我将带领读者一步一步地创建一个完整的WCF应用。
本应用功能虽然简单,但它涵盖了一个完整WCF应用的基本结构。
对那些对WCF不是很了解的读者来说,这个例子将带领你正式进入WCF的世界。
在这个例子中,我们将实现一个简单的计算服务(CalculatorService),提供基本的加、减、乘、除的运算。
和传统的分布式通信框架一样,WCF本质上提供一个跨进程、跨机器以致跨网络的服务调用。
在本例中,客户端和服务通过运行在相同的同一台机器上不同进程模拟,图1体现了客户端和服务端进程互相调用的关系。
图1计算服务应用运行环境
WCF的服务不能孤立地存在,需要寄宿于一个运行着的进程中,我们把承载WCF服务的进程称为宿主,为服务指定宿主的过程称为服务寄宿(ServiceHosting)。
在我们的计算服务应用中,采用了两种服务寄宿方式:
通过自我寄宿(Self-Hosting)的方式创建一个控制台应用作为服务的宿主(寄宿进程为Hosting.exe);通过IIS寄宿方式将服务寄宿于IIS中(寄宿进程为IIS的工作进行W3wp.exe)。
客户端通过另一个控制台应用模拟(进程为Client.exe)。
接下来,我们就一步一步来构建这样的一个WCF应用。
步骤一:
构建整个解决方案
通过VS2008创建一个空白的解决方案,添加如下四个项目。
项目的类型、承载的功能和相互引用关系如下,整个项目在VS下的结构如图2所示。
∙Contracts:
一个类库项目,定义服务契约(ServiceContract),引用System.ServiceMode程序集(WCF框架的绝大部分实现和API定义在该程序集中);
∙Services:
一个类库项目,提供对WCF服务的实现。
定义在该项目中的所有WCF服务实现了定义在Contracts中相应的服务契约,所以Services具有对Contracts项目的引用;
∙Hosting:
一个控制台(Console)应用,实现对定义在Services项目中的服务的寄宿,该项目须要同时引用Contracts和Services两个项目和System.ServiceMode程序集;
∙Client:
一个控制台应用模拟服务的客户端,该项目引用System.ServiceMode程序集。
图2计算服务在VS中的结构
步骤二:
创建服务契约
WCF采用基于契约的交互方式实现了服务的自治,以及客户端和服务端之间的松耦合。
WCF包含四种类型的契约:
服务契约、数据契约、消息契约和错误契约,这里着重于服务契约。
从功能上讲,服务契约抽象了服务提供的所有操作;而站在消息交换的角度来看,服务契约则定义了基于服务调用的消息交换过程中,请求消息和回复消息的结构,以及采用的消息交换模式。
第4章将提供对服务契约的详细介绍。
一般地,我们通过接口的形式定义服务契约。
通过下面的代码,将一个接口ICalculator定义成服务契约。
WCF广泛采用基于自定义特性(CustomAttribtue)的声明式编程模式,我们通过在接口上应用System.ServiceModel.ServiceContractAttribute特性将一个接口定义成服务契约。
在应用ServiceContractAttribute特性的同时,还可以指定服务契约的名称和命名空间。
至于契约名称和命名空间的含义和作用,在本人拙著《WCF技术剖析(卷1)》第4章,在这里我们将契约名称和命名空间设置成CalculatorService和
通过应用ServiceContractAttribute特性将接口定义成服务契约之后,接口的方法成员并不能自动成为服务的操作。
在此方面,WCF采用的是显式选择(ExplicitOpt-in)的策略:
我们须要在相应的操作方法上面显式地应用OperationContractAttribute特性。
1:
usingSystem.ServiceModel;
2:
namespaceArtech.WcfServices.Contracts
3:
{
4:
[ServiceContract(Name="CalculatorService",Namespace="
5:
publicinterfaceICalculator
6:
{
7:
[OperationContract]
8:
doubleAdd(doublex,doubley);
9:
10:
[OperationContract]
11:
doubleSubtract(doublex,doubley);
12:
13:
[OperationContract]
14:
doubleMultiply(doublex,doubley);
15:
16:
[OperationContract]
17:
doubleDivide(doublex,doubley);
18:
}
19:
}
步骤三:
创建服务
当服务契约成功创建时,我们需要通过实现服务契约来创建具体的WCF服务。
WCF服务CalculatorService定义在Services项目中,实现了服务契约接口ICalculator,实现了所有的服务操作。
CalculatorService定义如下:
1:
usingArtech.WcfServices.Contracts;
2:
namespaceArtech.WcfServices.Services
3:
{
4:
publicclassCalculatorService:
ICalculator
5:
{
6:
publicdoubleAdd(doublex,doubley)
7:
{
8:
returnx+y;
9:
}
10:
11:
publicdoubleSubtract(doublex,doubley)
12:
{
13:
returnx-y;
14:
}
15:
16:
publicdoubleMultiply(doublex,doubley)
17:
{
18:
returnx*y;
19:
}
20:
21:
publicdoubleDivide(doublex,doubley)
22:
{
23:
returnx/y;
24:
}
25:
}
26:
}
步骤四:
通过自我寄宿的方式寄宿服务
WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程。
WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段。
终结点由地址(Address)、绑定(Binding)和契约(Contract)三要素组成,如图3所示。
由于三要素应为首字母分别为ABC,所以就有了易于记忆的公式:
Endpoint=ABC。
一个终结包含了实现通信所必需的所有信息,我们可以这样认识终结点的ABC:
∙地址(Address):
地址决定了服务的位置,解决了服务寻址的问题,《WCF技术剖析(卷1)》第2章提供了对地址和寻址机制的详细介绍;
∙绑定(Binding):
绑定实现了通信的所有细节,包括网络传输、消息编码,以及其他为实现某种功能(比如安全、可靠传输、事务等)对消息进行的相应处理。
WCF中具有一系列的系统定义绑定,比如BasicHttpBinding、WsHttpBinding、NetTcpBinding等,《WCF技术剖析(卷1)》第3章提供对绑定的详细介绍;
∙契约(Contract):
契约是对服务操作的抽象,也是对消息交换模式以及消息结构的定义。
《WCF技术剖析(卷1)》第4章提供对服务契约的详细介绍。
图3终结点三要素
服务寄宿的目的就是开启一个进程,为WCF服务提供一个运行的环境。
通过为服务添加一个或多个终结点,使之暴露给潜给的服务消费者。
服务消费者最终通过相匹配的终结点对该服务进行调用。
我们可以完全通过代码的方式完成所有的服务寄宿工作,下面的代码体现了通过一个控制台应用对CalculatorService的寄宿:
1:
usingSystem;
2:
usingSystem.ServiceModel;
3:
usingSystem.ServiceModel.Description;
4:
usingArtech.WcfServices.Contracts;
5:
usingArtech.WcfServices.Services;
6:
namespaceArtech.WcfServices.Hosting
7:
{
8:
classProgram
9:
{
10:
staticvoidMain(string[]args)
11:
{
12:
using(ServiceHosthost=newServiceHost(typeof(CalculatorService)))
13:
{
14:
host.AddServiceEndpoint(typeof(ICalculator),newWSHttpBinding(),"http:
//127.0.0.1:
9999/calculatorservice");
15:
if(host.Description.Behaviors.Find()==null)
16:
{
17:
ServiceMetadataBehaviorbehavior=newServiceMetadataBehavior();
18:
behavior.HttpGetEnabled=true;
19:
behavior.HttpGetUrl=newUri("http:
//127.0.0.1:
9999/calculatorservice/metadata");
20:
host.Description.Behaviors.Add(behavior);
21:
}
22:
host.Opened+=delegate
23:
{
24:
Console.WriteLine("CalculaorService已经启动,按任意键终止服务!
");
25:
};
26:
27:
host.Open();
28:
Console.Read();
29:
}
30:
}
31:
}
32:
}
WCF服务寄宿通过一个特殊的对象完成:
ServiceHost。
在上面的例子中,基于WCF服务的类型(typeof(CalculatorService))创建了ServieHost对象,并添加了一个终结点。
具体的地址为http:
//127.0.0.1:
9999/calculatorservice,采用了WSHttpBinding,并指定了服务契约的类型ICalculator。
松耦合是SOA的一个基本的特征,WCF应用中客户端和服务端的松耦合体现在客户端只须要了解WCF服务基本的描述,而无须知道具体的实现细节,就可以实现正常的服务调用。
WCF服务的描述通过元数据(Metadata)的形式发布出来。
WCF中元数据的发布通过一个特殊的服务行为ServiceMetadataBehavior实现。
在上面提供的服务寄宿代码中,我们为创建的ServiceHost添加了ServiceMetadataBehavior,并采用了基于HTTP-GET的元数据获取方式,元数据的发布地址通过ServiceMetadataBehavior的HttpGetUrl指定。
在调用ServiceHost的Open方法对服务成功寄宿后,我们可以通过该地址获取服务相关的元数据。
在IE地址栏上键入http:
//127.0.0.1:
9999/calculatorservice/metadata,你将会得到以WSDL形式体现的服务元数据,如图4所示。
图4通过HTTP-GET的方式获取WCF服务的元数据
在进行真正的WCF应用开发时,一般不会直接通过编码的方式进行终结点的添加和服务行为的定义,而是通过配置的方式进行。
上面添加终结点和定义服务行为的代码可以用下面的配置代替:
1:
xmlversion="1.0"encoding="utf-8"?
>
2:
3:
4:
5:
6:
7:
//127.0.0.1:
9999/calculatorservice/metadata"/>
8:
9:
10:
11:
12:
13:
//127.0.0.1:
9999/calculatorservice"binding="wsHttpBinding"contract="Artech.WcfServices.Contracts.ICalculator"/>
14:
15:
16:
17:
对于初学者来说,WCF的配置显得过于复杂,直接对配置文件进行手工编辑不太现实。
在这种情况下,可以直接使用VS提供的配置工具。
你可以通过VS的工具(Tools)菜单,选择“WCFServiceConfigurationEditor”子项,开启这样的一个配置编辑器,如图5所示。
如果采用了上诉的配置,服务寄宿代码将会得到极大的精简,只需包含下面几行代码:
1:
namespaceArtech.WcfServices.Hosting
2:
{
3:
classProgram
4:
{
5:
staticvoidMain(string[]args)
6:
{
7:
using(ServiceHosthost=newServiceHost(typeof(CalculatorService)))
8:
{
9:
host.Opened+=delegate
10:
{
11:
Console.WriteLine("CalculaorService已经启动,按任意键终止服务!
");
12:
};
13:
14:
host.Open();
15:
Console.Read();
16:
}
17:
}
18:
}
19:
}
图5如何获得WCF服务配置编辑器
步骤五:
创建客户端调用服务
服务被成功寄宿后,服务端便开始了服务调用请求的监听工作。
此外,服务寄宿将服务描述通过元数据的形式发布出来,相应的客户端就可以获取这些元数据创建客户端程序进行服务的消费。
在VS下,当我们添加服务引用的时候,VS在内部帮我们实现元数据的获取,并借助这些元数据通过代码生成工具(SvcUtil.exe)自动生成用于服务调用的服务代理相关的代码和相应的配置。
在运行服务寄宿程序(Hosting.exe)的情况下,右键点击Client项目,在弹出的上下文菜单中选择“添加服务引用(AddServiceReferences)”,如图6所示的添加服务引用的对话会显示出来。
在地址栏上键入服务元数据发布的源地址:
http:
//127.0.0.1:
9999/calculatorservice/metadata,并指定一个命名空间,点击OK按钮,VS为为你生成一系列用于服务调用的代码和配置。
图6添加服务引用
在一系列自动生成的类中,包含一个服务契约接口、一个服务代理对象和其他相关的类。
被客户端直接用于服务调用的是一个继承自ClientBase并实现了CalculatorService接口(CalculatorService为客户端生成的服务契约接口类型)的服务代理类。
ClientBase的定义如下所示:
1:
namespaceArtech.WcfServices.Client.CalculatorServices
2:
{
3:
//其他类型成员
4:
[System.Diagnostics.DebuggerStepThroughAttribute()]
5:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel","4.0.0.0")]
6:
publicpartialclassCalculatorServiceClient:
System.ServiceModel.ClientBase,Artech.WcfServices.Client.CalculatorServices.CalculatorService{
7:
8:
publicCalculatorServiceClient(){
9:
}
10:
11:
publicCalculatorServiceClient(stringendpointConfigurationName):
12:
base(endpointConfigurationName){
13:
}
14:
15:
publicCalculatorServiceClient(stringendpointConfigurationName,stringremoteAddress):
16:
base(endpointConfigurationName,remoteAddress){
17:
}
18:
19:
publicCalculatorServiceClient(stringendpointConfigurationName,System.ServiceModel.EndpointAddressremoteAddress):
20:
base(endpointConfigurationName,remoteAddress){
21:
}
22:
23:
publicCalculatorServiceClient(System.ServiceModel.Channels.Bindingbinding,System.ServiceModel.EndpointAddressremoteAddress):
24:
base(binding,remoteAddress){
25:
}
26:
27:
publicdoubleAdd(doublex,doubley){
28:
returnbase.Channel.Add(x,y);
29:
}
30:
31:
publicdoubleSubtract(doublex,doubley){
32:
returnbase.Channel.Subtract(x,y);
33:
}
34:
35:
publicdoubleMultiply(doublex,doubley){
36:
returnbase.Channel.Multiply(x,y);
37:
}
38:
39:
publicdoubleDivide(doublex,doubley){
40:
returnbase.Channel.Divide(x,y);
41:
}
42:
}
我们可以创建CalculatorServiceClient对象,执行相应方法调用服务操作。
客户端进行服务调用的代码如下:
1:
usingSystem;
2:
usingArtech.WcfServices.Client.CalculatorServices;
3:
namespaceArtech.WcfServices.Client
4:
{
5:
classProgram
6:
{
7:
staticvoidMain(string[]args)
8:
{
9:
using(CalculatorServiceClientproxy=newCalculatorServiceClient())
10:
{
11:
Console.WriteLine("x+y={2}whenx={0}andy={1}",1,2,proxy.Add(1,2));
12:
Console.WriteLine("x-y={2}whenx={0}andy={1}",1,2,proxy.Subtract(1,2));
13:
Console.WriteLine("x*y={2}whenx={0}andy={1}",1,2,proxy.Multiply(1,2));
14:
Console.WriteLine("x/y={2}whenx={0}andy={1}",1,2,proxy.Divide(1,2));
15:
}
16:
}
17:
}
18:
}
运行后输出:
x+y=3whenx=1andy=2
x-y=-1whenx=1andy=2
x*y=2whenx=1andy=2
x/y=0.5whenx=1andy=2
客户端通过服务代理对象进行服务的调用,上面的例子通过创建自动生成的、继承自ClientBase的类型对象进行服务调用。
实际上,我们还具有另外一种创建服务代理的方法,就是通过ChannelFactory。
此外,WCF采用基于契约的服务调用方法,从上面的例子我们也可以看到,VS在进行服务引用添加的过程中,会在客户端创建一个与服务端等效的服务契约接口。
在我们的例子中,由于服务端和客户端都是在同一个解决方案中,完全可以让服务端和客户端引用相同的契约。
为了演示这种场景,我们将添加的服务引用移除,并为C