文档库 最新最全的文档下载
当前位置:文档库 › WCF教程_C#

WCF教程_C#

WCF教程_C#
WCF教程_C#

WCF

Windows Communication Foundation入门(Part One)

前言:WCF是微软基于SOA(Service Oriented Architecture)推出的.Net平台下的框架产品,它代表了软件架构设计与开发的一种发展方向,在微软的战略计划中也占有非常重要的地位。了解和掌握WCF,对于程序员特别是基于微软产品开发的程序员而言,是非常有必要的。对于WCF,笔者也是初窥门径,抱着学习的态度作这样的一个介绍。文中的内容主要参考了微软官方的文档、资料,以及众多介绍WCF的技术资料。这些资料主要都是英文,不便于国内程序员学习WCF。虽然本人才疏学浅,却愿意作这样的介绍者。由于自己仅是一个初学者,英文的功底也不够深厚,所以文中难免会有疏漏之处。同时,我也希望在文中尽量表达出自己的一些心得与见解,这就不免增加了出现错误的可能性。此外,由于WCF至今仍未有正式的版本,文中相关的技术描述以及代码会根据版本的不同而发生变化,所以我也只能尽量对此给与一定的说明。本文会是多篇文章拼凑在一起的系列,说是系列,但并没有严格的渐进关系,只是整体上希望能有一个相对全面的WCF入门介绍。此外,笔者也希望能通过此文抛砖引玉,这样也能让我的WCF学习之旅更轻松一点。

一、什么是WCF?

根据微软官方的解释,WCF(之前的版本名为“Indigo”)是使用托管代码建立和运行面向服务(Service Oriented)应用程序的统一框架。它使得开发者能够建立一个跨平台的安全、可信赖、事务性的解决方案,且能与已有系统兼容协作。WCF是微软分布式应用程序开发的集大成者,它整合了.Net平台下所有的和分布式系统有关的技术,例如.Net Remoting、ASMX、WSE和MSMQ。以通信(Communiation)范围而论,它可以跨进程、跨机器、跨子网、企业网乃至于Internet;以宿主程序而论,可以以https://www.wendangku.net/doc/7c8917401.html,,EXE,WPF,Windows Forms,NT Service,COM+作为宿主(Host)。WCF可以支持的协议包括TCP,HTTP,跨进程以及自定义,安全模式则包括SAML,Kerberos,X509,用户/密码,自定义等多种标准与模式。也就是说,在WCF框架下,开发基于SOA的分布式系统变得容易了,微软将所有与此相关的技术要素都包含在内,掌握了WCF,就相当于掌握了叩开SOA大门的钥匙。

WCF是建立在.Net Framework 2.0基础之上的,正式的版本应该会作为Windows Vista 的核心部分而Release。然而,这并不代表WCF只能运行在Windows Vista下。只要安装了WinFX Runtime Components,在Windows XP和Windows 2003操作系统下,仍然可以使用。Visual Studio 2005中并没有包含WCF,但是当安装好了WinFX Runtime

Components后,我们就可以在Visual Studio 2005环境下开发和创建WCF的程序了。

目前最新的WCF版本是February 2006 CTP,下载页面是:

https://www.wendangku.net/doc/7c8917401.html,/downloads/details.aspx?FamilyId=F51C4D96-9AEA-47 4F-86D3-172BFA3B828B&displaylang=en。使用WCF需要用到一些相关的工具,如SvcUtil.exe,所以还需要下载WinFX Runtime Components的SDK,其下载页面是:https://www.wendangku.net/doc/7c8917401.html,/downloads/details.aspx?FamilyId=9BE1FC7F-0542-47 F1-88DD-61E3EF88C402&displaylang=en。安装SDK可以选择网络安装或本地安装。如果是本地安装,文件大小为1.1G左右,是ISO文件。安装了SDK后,在program files目录下,有microsoft SDK目录。

WCF是微软重点介绍的产品,因此也推出了专门的官方网站

(https://www.wendangku.net/doc/7c8917401.html,),该网站有最新的WCF新闻发布,以及介绍WCF 的技术文档和样例代码。

二、WCF的优势

在David Chappell所撰的《Introducing Windows Communication Foundation》一文中,用了一个活鲜鲜的例子,来说明WCF的优势所在。假定我们要为一家汽车租赁公司开发一个新的应用程序,用于租车预约服务。该租车预约服务会被多种应用程序访问,包括呼叫中心(Call Center),基于J2EE的租车预约服务以及合作伙伴的应用程序(Partner Application),如图所示:

呼叫中心运行在Windows平台下,是在.Net Framework下开发的应用程序,用户为公司员工。由于该汽车租赁公司兼并了另外一家租赁公司,该公司原有的汽车预约服务应用程序是

J2EE应用程序,运行在非Windows操作系统下。呼叫中心和已有的汽车预约应用程序都运行在Intranet环境下。合作伙伴的应用程序可能会运行在各种平台下,这些合作伙伴包括旅行社、航空公司等等,他们会通过Internet来访问汽车预约服务,实现对汽车的租用。

这样一个案例是一个典型的分布式应用系统。如果没有WCF,利用.Net现有的技术应该如何开发呢?

首先考虑呼叫中心,它和我们要开发的汽车预约服务一样,都是基于.Net Framework的应用程序。呼叫中心对于系统的性能要求较高,在这样的前提下,.Net Remoting是最佳的实现技术。它能够高性能的实现.Net与.Net之间的通信。

要实现与已有的J2EE汽车预约应用程序之间的通信,只有基于SOAP的Web Service可以实现此种目的,它保证了跨平台的通信;而合作伙伴由于是通过Internet来访问,利用https://www.wendangku.net/doc/7c8917401.html, Web Service,即ASMX,也是较为合理的选择,它保证了跨网络的通信。由于涉及到网络之间的通信,我们还要充分考虑通信的安全性,利用WSE(Web Service

Enhancements)可以为ASMX提供安全的保证。

一个好的系统除了要保证访问和管理的安全,高性能,同时还要保证系统的可信赖性。因此,事务处理是企业应用必须考虑的因素,对于汽车预约服务而言,同样如此。在.Net中,Enterprise Service(COM+)提供了对事务的支持,其中还包括分布式事务(Distributed Transactions)。不过对于Enterprise Service而言,它仅支持有限的几种通信协议。

如果还要考虑到异步调用、脱机连接、断点连接等功能,我们还需要应用MSMQ(Microsoft Message Queuing),利用消息队列支持应用程序之间的消息传递。

如此看来,要建立一个好的汽车租赁预约服务系统,需要用到的.Net分布式技术,包括.Net Remoting、Web Service,COM+等五种技术,这既不利于开发者的开发,也加大了程序的维护难度和开发成本。正是因应于这样的缺陷,WCF才会在.Net 2.0中作为全新的分布式开发技术被微软强势推出,它整合了上述所属的分布式技术,成为了理想的分布式开发的解决之道。下图展示了WCF与之前的相关技术的比较:

从功能的角度来看,WCF完全可以看作是ASMX,.Net Remoting,Enterprise Service,WSE,MSMQ等技术的并集。(注:这种说法仅仅是从功能的角度。事实上WCF远非简单的并集这样简单,它是真正面向服务的产品,它已经改变了通常的开发模式。)因此,对于上述汽车预约服务系统的例子,利用WCF,就可以解决包括安全、可信赖、互操作、跨平台通信等等需求。开发者再不用去分别了解.Net Remoting,ASMX等各种技术了。

概括地说,WCF具有如下的优势:

1、统一性

前面已经叙述,WCF是对于ASMX,.Net Remoting,Enterprise Service,WSE,MSMQ 等技术的整合。由于WCF完全是由托管代码编写,因此开发WCF的应用程序与开发其它的.Net 应用程序没有太大的区别,我们仍然可以像创建面向对象的应用程序那样,利用WCF来创建面向服务的应用程序。

2、互操作性

由于WCF最基本的通信机制是SOAP,这就保证了系统之间的互操作性,即使是运行不同的上下文中。这种通信可以是基于.Net到.Net间的通信,如下图所示:

可以跨进程、跨机器甚至于跨平台的通信,只要支持标准的Web Service,例如J2EE应用服务器(如WebSphere,WebLogic)。应用程序可以运行在Windows操作系统下,也可以运行在其他的操作系统,如Sun Solaris,HP Unix,Linux等等。如下图所示:

3、安全与可信赖

WS-Security,WS-Trust和WS-SecureConversation均被添加到SOAP消息中,以用于用户认证,数据完整性验证,数据隐私等多种安全因素。

在SOAP的header中增加了WS-ReliableMessaging允许可信赖的端对端通信。而建立在WS-Coordination和WS-AtomicTransaction之上的基于SOAP格式交换的信息,则支持两阶段的事务提交(two-phase commit transactions)。

上述的多种WS-Policy在WCF中都给与了支持。对于Messaging而言,SOAP是Web Service的基本协议,它包含了消息头(header)和消息体(body)。在消息头中,定义了WS-Addressing用于定位SOAP消息的地址信息,同时还包含了MTOM(消息传输优化机制,Message Transmission Optimization Mechanism)。如图所示:

4、兼容性

WCF充分的考虑到了与旧有系统的兼容性。安装WCF并不会影响原有的技术如ASMX 和.Net Remoting。即使对于WCF和ASMX而言,虽然两者都使用了SOAP,但基于WCF 开发的应用程序,仍然可以直接与ASMX进行交互。

Windows Communication Foundation入门(Part Two)

三、WCF的技术要素

作为基于SOA(Service Oriented Architecture)的一个框架产品,WCF最重要的就是能够快捷的创建一个服务(Service)。如下图所示,一个WCF Service由下面三部分构成:

1、Service Class:一个标记了[ServiceContract]Attribute的类,在其中可能包含多个方法。除了标记了一些WCF特有的Attribute外,这个类与一般的类没有什么区别。

2、Host(宿主):可以是应用程序,进程如Windows Service等,它是WCF Service运行的环境。

3、Endpoints:可以是一个,也可以是一组,它是WCF实现通信的核心要素。

WCF Service由一个Endpoints集合组成,每个Endpoint就是用于通信的入口,客户端和服务端通过Endpoint交换信息,如下图所示:

从图中我们可以看到一个Endpoint由三部分组成:Address,Binding,Contract。便于记忆,我们往往将这三部分称为是Endpoint的ABCs。

Address是Endpoint的网络地址,它标记了消息发送的目的地。Binding描述的是如何发送消息,例如消息发送的传输协议(如TCP,HTTP),安全(如SSL,SOAP消息安全)。Contract 则描述的是消息所包含的内容,以及消息的组织和操作方式,例如是one-way,duplex和request/reply。所以Endpoint中的ABCs分别代表的含义就是:where,how,what。当WCF发送消息时,通过address知道消息发送的地址,通过binding知道怎样来发送它,通过contract则知道发送的消息是什么。

在WCF中,类ServiceEndpoint代表了一个Endpoint,在类中包含的EndpointAddress,Binding,ContractDescription类型分别对应Endpoint的Address,Binding,Contract,如下图:

EndpointAddress类又包含URI,Identity和可选的headers集合组成,如下图:

Endpoint安全的唯一性识别通常是通过其URI的值,但为了避免一些特殊情况造成URI的重复,又引入了Identity附加到URI上,保证了Endpoint地址的唯一性。至于可选的AddressHeader则提供了一些附加的信息,尤其是当多个Endpoint在使用同样的URI地址信息时,AddressHeader就非常必要了。

Binding类包含Name,Namespace和BindingElement集合,如下图:

Binding的Name以及Namespace是服务元数据(service’s metadata)的唯一标识。BindingElement描述的是WCF通信时binding的方式。例如,SecurityBindingElement 表示Endpoint使用SOAP消息安全方式,而ReliableSessionBindingElement表示Endpoint利用可信赖消息确保消息的传送。TcpTransportBindingElement则表示Endpoint 利用TCP作为通信的传输协议。每种BindingElement还有相应的属性值,进一步详细的描述WCF通信的方式。

BindingElement的顺序也非常重要。BindingElement集合通常会创建一个用于通信的堆栈,其顺序与BindingElement集合中元素顺序一致。集合中最后一个binding element对应于通信堆栈的底部,而集合中的第一个binding element则对应于堆栈的顶端。入消息流的方向是从底部经过堆栈向上,而出消息流的方向则从顶端向下。因此,BindingElement集合中的binding element顺序直接影响了通信堆栈处理消息的顺序。幸运的是,WCF已经提供了一系列预定义的Binding,能够满足大多数情况,而不需要我们自定义Binding,殚精竭虑地考虑binding element的顺序。

Contract是一组操作(Operations)的集合,该操作定义了Endpoint通信的内容,每个Operation都是一个简单的消息交换(message exchange),例如one-way或者request/reply消息交换。

类ContractDescription用于描述WCF的Contracts以及它们的操作operations。在ContractDescription类中,每个Contract的operation都有相对应的

OperationDescription,用于描述operation的类型,例如是one-way,还是request/reply。在OperationDescription中还包含了MessageDecription集合用于描述message。

在WCF编程模型中,ContractDescription通常是在定义Contract的接口或类中创建。对于这个接口或类类型,标记以ServiceContractAttribute,而其Operation方法则标记以OperationContractAttribute。当然我们也可以不利用CLR的attribute,而采用手工创建。与Binding一样,每个Contract也包含有Name和Namespace,用于在Service的元数据中作为唯一性识别。此外,Contract中还包含了ContractBehavior的集合,ContractBehavior 类型可以用于修改或扩展contract的行为。类ContractDescription的组成如下图所示:

正如在ContractDescription中包含的IContractBehavior一样,WCF专门提供了行为Behavior,它可以对客户端和服务端的一些功能进行修改或者扩展。例如ServiceMetadataBehavior用于控制Service是否发布元数据。相似的,security behavior 用于控制安全与授权,transaction behavior则控制事务。

除了前面提到的ContractBehavior,还包括ServiceBehavior和ChannelBehaivor。ServiceBehavior实现了IServiceBehavior接口,ChannelBehaivor则实现了IChannleBehavior接口。

由于WCF需要管理的是服务端与客户端的通信。对于服务端,WCF提供了类ServiceDescription用于描述一个WCF Service,;而针对客户端,WCF管理的是发送消息时需要使用到的通道Channel,类ChannelDescription描述了这样的客户端通道。ServiceDescription类的组成如下图所示:

我们可以利用代码的方式创建ServiceDescription对象,也可以利用WCF的Attribute,或者使用工具SvcUtil.exe。虽然可以显式的创建它,但通常情况下,它会作为运行中的Service 一部分而被隐藏于后(我在后面会提到)。

ChannelDescription类的组成与ServiceDescription大致相同,但它仅仅包含了一个ServiceEndpoint,用于表示客户端通过通道通信的目标Endpoint。当然,施加到ChannelDescription的Behavior也相应的为IChannelBehavior接口类型,如图所示:

定义一个WCF Service非常简单,以Hello World为例,定义的Service可能如下:using System.ServiceModel

[ServiceContract]

public class HelloWorld

{

[OperationContract]

public void Hello()

{

Console.WriteLine(“Hello World!”);

}

}

System.ServiceModel是微软为WCF提供的一个新的类库,以用于面向服务的程序设计。在开发WCF应用程序时,需要先添加对System.ServiceModel的引用。WCF中的大部分类和接口也都是在命名空间System.ServiceModel下。

我们为HelloWorld类标记了[ServiceContract],这就使得该类成为了一个WCF Service,而其中的方法Hello()则因为标记了[OperationContract],而成为该Service的一个Operation。

不过WCF推荐的做法是将接口定义为一个Service,这使得WCF Service具有更好的灵活性,毕竟对于一个接口而言,可以在同时有多个类实现该接口,这也就意味着可以有多个Service Contract的实现。那么上面的例子就可以修改为:

[ServiceContract]

public interface IHello

{

[OperationContract]

void Hello();

}

而类HelloWorld则实现该IHello接口:

public class HelloWorld:IHello

{

public void Hello()

{

Console.WriteLine(“Hello World!”);

}

}

注意在实现了IHello接口的类HelloWorld中,不再需要在类和方法中标注ServiceContractAttribute和OperationContractAttribute了。

前面我已经提过,一个WCF Service必须有host作为它运行的环境。这个host可以是https://www.wendangku.net/doc/7c8917401.html,,可以是Windows Service,也可以是一个普通的应用程序,例如控制台程序。下面就是一个Host的实现:

using System.ServiceModel

public class HostApp

{

static void Main(string[] args)

{

ServiceHost host = new ServiceHost(typeof(HelloWorld), new

Uri(“http://localhost:8080/HelloService”));

host.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(),”Svc”);

host.Open();

Console.WriteLine(“Start Your Service.”);

Console.ReadKey();

host.Close();

}

}

在这个HostApp中,我们为HelloWorld创建了一个ServiceHost对象。通过它就可以创建WCF运行时(Runtime),WCF Runtime是一组负责接收和发送消息的对象。ServiceHost 可以创建SerivceDescription对象,利用SerivceDescription,SercieHost为每一个ServiceEndpoint创建一个EndpointListener。ServiceHost的组成如下图:

EndpointListener侦听器包含了listening address,message filtering和dispatch,它们对应ServiceEndpoint中的EndpointAddress,Contract和Binding。在EndpointListener 中,还包含了一个Channel Stack,专门负责发送和接收消息。

注意在创建ServiceHost时,传递的type类型参数,不能是interface。因此,我在这里传入的是typeof(HelloWorld)。ServiceHost类的AddServiceEndpoint()方法实现了为Host

添加Endpoint的功能,其参数正好是Endpoint的三部分:Address,Bingding和Contract。(此时的IHello即为ServiceContract,其方法Hello为OperationContract)。ServiceHost的Open()方法用于创建和打开Service运行时,而在程序结束后我又调用了Close()方法,来关闭这个运行时。实际上以本例而言,该方法可以不调用,因为在应用程序结束后,系统会自动关闭该host。但作为一种良好的编程习惯,WCF仍然要求显式调用Close()方法,因为Service运行时其本质是利用Channel来完成消息的传递,当打开一个Service运行时的时候,系统会占用一个Channel,调用完后,我们就需要释放对该通道的占用。当然我们也可以用using语句来管理ServiceHost资源的释放。

定义好了一个WCF Service,并将其运行在Host上后,如何实现它与客户端的通信呢?典型的情况下,服务端与客户端均采用了Web Service Description Language(WSDL),客户端可以通过工具SvcUtil.exe生成对应于该WCF Service的Proxy代码,以完成之间的消息传递,如图所示:

SvcUtil.exe是由WinFx Runtime Component SDK所提供的,如果安装SDK正确,可以在其中找到该应用工具。生成客户端Proxy代码的方法很简单,首先需要运行服务端Service。然后再命令行模式下运行下面的命令:

SvcUtil http://localhost:8080/HelloService

这样会在当前目录下产生两个文件output.cs和output.config。前者最主要的就是包含了一个实现了IHello接口的Proxy对象,这个代理对象名为HelloProxy,代码生成的结果如下:[https://www.wendangku.net/doc/7c8917401.html,piler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

public partial class HelloProxy : System.ServiceModel.ClientBase, IHello {

public HelloProxy()

{

}

public HelloProxy(string endpointConfigurationName) :

base(endpointConfigurationName)

{

}

public HelloProxy(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress)

{

}

public HelloProxy(string endpointConfigurationName,

System.ServiceModel.EndpointAddress remoteAddress) :

base(endpointConfigurationName, remoteAddress)

{

}

public HelloProxy(System.ServiceModel.Channels.Binding binding,

System.ServiceModel.EndpointAddress remoteAddress) :

base(binding, remoteAddress)

{

}

public void Hello()

{

base.InnerProxy.Hello();

}

}

(注:本程序在WinFx 2006 February CTP版本下运行通过)

至于后者,则是WCF Service的配置信息,主要包含的是Endpoint中Address,Binding以及Contract的配置(在后续文章我会详细介绍)。

现在客户端就可以直接使用HelloProxy对象,来完成与服务端的通信了:

public class ClientApp

{

static void Main(string[] args)

{

using (HelloProxy proxy = new HelloProxy())

{

proxy.Hello();

}

Console.ReadKey();

}

}

除了可以使用SvcUtil工具产生客户端代码,同样我们也可以利用代码的方式来完成客户端。客户端在发送消息给服务端时,其通信的基础是Service的Endpoint,WCF提供了System.ServiceModel.Description.ServiceEndpoint类,通过创建它来实现两端的通信。在前面,我还提到“对于客户端而言,WCF管理的是发送消息时需要使用到的通道Channel”,为此,WCF提供了ChannelFactory(其命名空间为System.ServiceModel.Channel),专门用于创建客户端运行时(runtime)。ChannelFactory与ServiceHost相对应,它可以创建ChannelDescription对象。与服务端ServiceHost不同的是,客户端并不需要侦听器,因为客户端往往是建立连接的“发起方”,并不需要侦听进来的连接。因此客户端的Channel Stack 会由ChannelDescription创建。

ChannelFactory和ServiceHost都具有Channel Stack,而服务端与客户端的通信又是通过channel来完成,这就意味着,利用ChannelFactory,客户端可以发送消息到服务端。而客户端本身并不存在Service对象,因此该Service的Proxy,是可以通过Channel来得到的。所以客户端的代码可以修改如下:

using System.ServiceModel;

using System.ServiceModel.Description;

using System.ServiceModel.Channel

public class ClientApp

{

static void Main(string[] args)

{

ServiceEndpoint httpEndpoint = new

ServiceEndpoint(ContractDescription.GetContract(typeof(IHello)), new BasicHttpBinding(), new

EndpointAddress(“http://localhost:8080/HelloService/Svc”));

using (ChannelFactory factory = new

ChannelFactory(httpEndPoint))

{

//创建IHello服务的代理对象;

IHello service = factory.CreateChannel();

service.Hello();

}

Console.ReadKey();

}

}

(注:本程序在WinFx 2006 February CTP版本下运行通过)

对于上面的代码,我们有两点需要注意:

1、采用这种方式,前提条件是客户端能够访问IHello接口。这也印证了之前我所叙述的最好使用interface来定义Service的好处。此外,为了保证部署的方便,有关Service的interface 最好单独编译为一个程序集,便于更好的部署到客户端。

2、客户端必须知道服务端binding的方式以及address。

对于服务端而言,我们也可以直接在浏览器中打开该Service,在地址栏中输入

http://localhost:8080/HelloService,如下图:

点击链接:http://localhost:8080/HelloService?wsdl,我们可以直接看到HelloService 的WSDL。注意到在这里我并没有使用IIS,实际上WCF内建了对httpsys的集成,允许任何应用程序自动成为HTTP listener。

Windows Communication Foundation入门(Part Three)

《Windows Communication Foundation之旅》系列之三

示例代码下载:DuplexSample.rar

四、Service Contract编程模型

在Part Two中,我以“Hello World”为例讲解了如何定义一个Service。其核心就是为接口或类施加ServiceContractAttribute,为方法施加OperationContractAttribute。在Service 的方法中,可以接受多个参数,也可以有返回类型,只要这些数据类型能够被序列化。这样一种

相关文档