WCF是基于Windows平台下开发和部署服务的SDK,为服务提供了运行时环境,使得开发者能够将CLR类型公开为服务,又能够以CLR类型的方式使用服务
服务是公开的一组功能的集合
SOA(Service-oriented applications):面向服务应用程序
面向服务的应用程序(SOA)将众多服务聚集到一个单一逻辑的应用程序中,这就类似于面向组件的应用程序聚合组件,或者面向对象的应用程序聚合对象,如图:
服务之间的交互,只允许指定的通信模式
客户端与服务端通过消息的发送与接收进行交互,WCF中的所有消息均为SOAP(Simple Object Access Protocol)消息
WCF服务可以在不同的协议之间传输,而不仅限于HTTP
WCF不允许客户端直接与服务交互,即使它调用的是本地机器内存中的服务。相反,客户端总是使用代理(proxy)将调用转发给服务。代理公开的操作与服务相同
使用proxy可以将客户端与服务端的调用地址做解耦
不管是远程还是本地调用WCF,都统一使用代理的形式,这样统一一种规则可以避免不同方式调用产生的各种问题
WCF的每一个服务都具有一个唯一的地址(Address)。地址包含两个重要元素:服务位置与传输协议(transport protocol),或者是用于服务通信的传输样式(transport schema)
WCF支持下列传输样式:HTTP、TCP、Peer network(对等网)、IPC (内部进程通信) 、MSMQ
地址的格式:
[传输协议]://[机器名或域名][:可选端口]/[可选的URI]
WCF的所有服务都会公开为契约(Contract)。契约与平台无关,是描述服务功能的标准方式
契约的类型有四种:服务契约(ServiceContract)、数据契约、错误契约、消息契约(很少使用)
服务契约例子(定义、实现):
对于没有使用OperationContract特性标识的方法,不会成为契约的一部分
将ServiceContract特性应用在内部(internal)接口上,该接口同样会被公开为公有服务契约。如果接口没有标记ServiceContract特性,WCF客户端则无法访问它(即使接口是公有的)。这一特点遵循了面向服务的一个原则即服务边界应该是明确的。为满足这一原则,所有契约必须明确要求:只有接口(或者类可以被标记为ServiceContract特性,从而被定义为WCF服务,其他类型都不允许
通过应用OperationContract特性,可以将契约方法暴露为逻辑操作,使其成为服务契约的一部分。接口(或类)中的其他方法如果没有应用OperationContract特性,则与契约无关
契约操作不能使用引用对象作为参数,只允许使用基本类型或数据契约(所以这里还是有很大灵活空间,引用类型只要你有数据契约就可以作为参数)
当接口应用了ServiceContract特性后,需要定义类实现该接口,定义的实现类的代码无需修改,自然而然成为一个WCF服务(它看起来就是一个普通的类)
WCF只能使用默认构造函数。同样,虽然类可以使用内部(internal)(译注2)[插图]的属性、索引器以及静态成员,但WCF客户端却无法访问它们。
可以直接将ServiceContract特性应用到服务类上(但是,应尽量避免将ServiceContract特性直接应用到服务类上,而应该定义一个单独的契约,这有利于在不同场景下使用契约):
托管WCF服务的进程是宿主进程,宿主可以由IIS(internet information services)提供
优势是宿主进程可以在客户端提交第一次请求的时候自动启动,缺点则在于只能使用HTTP协议
在IIS中托管服务需要在IIS下创建一个虚拟目录,并提供一个.svc文件,该文件的作用是识别到对应的服务代码:
使用IIS 5/6托管,服务的基地址必须与.svc文件的地址保持一致
在console程序中使用WCF服务,程序和服务都在一个进程中
简单的建立一个IIS服务,并启动:
可以使用ServiceHost中的一些委托,来输出一些日志,以说明服务的生命周期阶段
WCF引入绑定(Binding)技术是为了将繁杂的通信特征组合在一起,实现统一管理
一个绑定封装了诸如传输协议、消息编码、通信模式、可靠性、安全性、事务传播以及互操作性等相关选项的集合,使得它们保持一致
绑定将所有繁杂的基础功能模块从服务代码中解放出来,允许服务只需要关注业务逻辑的实现
绑定使得开发者能够基于不同的基础功能模块使用相同的服务逻辑
使用哪种绑定可以参考如下:
地址定义了服务的位置,绑定定义了服务通信的方式,契约则定义了服务的内容
终结点就是地址、契约与绑定的混成品。每一个终结点都包含了三个元素,而宿主则负责公开终结点,从逻辑上讲,终结点相当于服务的接口
每个服务至少必须公开一个业务终结点,每个终结点有且只能拥有一个契约。服务上的所有终结点都包含了唯一的地址,而一个单独的服务则可以公开多个终结点。这些终结点可以使用相同或不同的绑定,公开相同或不同的契约
使用配置文件的方式配置终结点:
编程方式配置终结点:
服务有两种方案可以发布自己的元数据。一种是基于HTTP-GET协议提供元数据
WCF能够为服务自动提供基于HTTP-GET的元数据,但需要显式地添加服务行为(Behavior)以支持这一功能,如下:
这样设置之后,我们可以使用HTTP访问:
编程方式启用元数据:
客户端使用了WCF,调用操作的常见做法是使用代理
①直接在vs ide上生成代理:
之后直接输入服务地址即可
②使用SvcUtil(SvcUtil.exe是.net自带的工具)
指令:SvcUtil 服务地址 指令选项
以上两种方式都会生成两个文件:一个是客户端需要的配置文件(建议不自动生成配置文件,而是手动配置管理),一个就是代理类
客户端的终结点配置基本与宿主相同
进程内托管配置服务端和客户端的配置在一个配置文件中
右键App.config使用Edit WCF Configuration,可快捷编辑配置文件
ClientBase
使用代理例子:
如果一个契约在配置文件中只包含一个终结点,那么在初始化客户端实例的时候不需要传入具体的Endpoint信息,如果一个契约在配置文件中包含多个终结点,那么在初始化客户端实例的时候需要传入具体的Endpoint信息
如果类继承了IDisposable接口,可以使用using语法,就不需要手动执行Dispose和Close操作
WCF服务端对于客户端默认的超时时间是1min,可以使用配置自定义超时时间
管理配置方式允许开发者在部署服务之后,修改服务与客户端的主要特性,而不需要重新编译或重新部署。主要缺陷则是不具备类型安全,只有在运行时才能发现配置的错误
可以直接使用通道调用服务的操作,而无须借助于代理类:
传输可靠性依靠TCP进行点对点传递
消息可靠性提供了端对端保证传递,确保消息的顺序无误
WCF的可靠性在绑定中控制与配置的
配置方式:
编码方式:
使用DeliveryRequirement特性,该定义可以定义到服务级,也可以定义到契约级
在WCF中,这样的重载是不支持的,需要使用OperationContract的Name属性的别名以获取支持
契约之间可以继承,但是ServiceContract特性是不能继承的,所以接口层级中的每级接口都必须显示的标记该特性
在面向服务的应用程序中,一个可重用的基本单元就是服务契约
服务元数据允许客户端将与平台、技术无关的表示形式转换为客户端本地的表示形式
如果标记了Serializable特性的类中有不支持序列化的内容,需要给它加上NonSerializable特性:
NonSerialized不局限于标记不可序列化的内容,如果序列化类中有任何内容不需要做序列化都可以加上这个特性
为了能够在操作中使用定制类型参数,必须符合两个条件:
Serializable的弊端:
WCF提供的新的面向服务特性:DataContract
DataContract只能够做到将类型参与到数据契约中,如下:
在客户端使用该结构体时,不能够为成员赋值:
如果想要序列化类型的成员,需要使用DataMember特性
在客户端就可以为该结构体下的成员赋值了:
在服务端的类型虽然标记的是Serializable特性,但是通过对客户端导入元数据,解析该类型时,导入的定义依然会使用DataContract特性:
服务端:
客户端:
从上面可以看到,既有Serializable特性又有DataContract特性,这是因为传统的格式器不能序列化只标记了DataContract特性的类型,必须两个特性同时定义在类型上才可序列化