WCF服务编程规范.docx
- 文档编号:8308774
- 上传时间:2023-01-30
- 格式:DOCX
- 页数:15
- 大小:87.92KB
WCF服务编程规范.docx
《WCF服务编程规范.docx》由会员分享,可在线阅读,更多相关《WCF服务编程规范.docx(15页珍藏版)》请在冰豆网上搜索。
WCF服务编程规范
WCF服务规范
一目标
业务处理与界面呈现分离,C/S与B/S结构的程序能通用转换和数据共享,各个系统可以资源共享
二软件构架
三WCF介绍
3.1、WCF的运行机制
如果从宏观的角度来分析WCF的运行机制,它的实现并不复杂。
WCF的体系架构是基于一种拦截机制来实现的,负责传递和拦截消息的组件为通道,在客户端发出对服务端服务的调用时,首先会通过一个服务代理对象,将调用方提供的对象序列化到消息中,然后该消息则通过通道进行传递。
通道不只是包括一个,而是多个通道对消息进行处理,包括传输、消息编码、管理会话、传播事务等,但最底层的通道总是传输通道。
这些通道的构成形成了一个通道堆栈。
由于对象已经被序列化,因而此时通道传递的消息可以跨进程或机器进行传递,利用传输通道传递到服务端。
服务端的构成与客户端基本相似,仍然是通过通道栈中最底层的传输通道接收消息,然后解析消息编码,并一层层地往上传输。
在服务端的通道栈之上,则是一个分发器(Dispatcher,或者说是调度器),它会首先对消息进行检查,然后选择一个客户端要调用的操作。
在这个过程中,消息会被反序列化。
下图说明了WCF的整个运行过程:
由于WCF通过通道的方式传递消息,整个通道同时担当了侦听器和拦截器的功能,它可以根据服务的定义,在方法执行的前或后执行不同的操作,例如事务、会话管理、安全等。
这些操作在WCF中,大多数都可以以Attribute的方式应用到服务契约上,这样的实现方式,就类似于采用了AOP(面向服务编程)的方法为服务提供了大量的基础功能,有助于简化服务开发者的工作。
3.2、为什么我们要选用WCF
在Windows平台下,尤其是在.NET平台下开发面向服务的应用程序,或者开发分布式系统,最佳选择就是WCF。
为什么呢?
原因就在于WCF涵盖了之前微软推出的所有用于分布式开发的技术,包括Remoting、WebServices、WSE、MSMQ等,并以一种统一的编程模式来实现。
WCF既支持具有互操作性的Web服务,也能够实现.NET客户端与.NET服务端的通信,提供了分布式事务的支持,同时在安全性上,它完全遵循了WS-*的标准,此外,它还支持队列服务,可以非常方便地利用消息队列完成异步操作与脱机调用。
而这些功能,以前的技术都只是部分的实现。
如下表所示:
特性
WebService
.NETRemoting
EnterpriseServices
WSE
MSMQ
WCF
具有互操作性的Web服务
支持
支持
.NET到.NET的通信
支持
支持
分布式事务
支持
支持
支持WS标准
支持
支持
消息队列
支持
支持
WCF同时也使得面向服务编程更加简单而统一了。
如果采用旧有的技术,由于各种技术的编程模型完全不一致,使得程序的迁移非常的困难。
例如,最初采用.NETRemoting技术开发的分布式系统,由于业务需求的变化,要求发布具有互操作性的Web服务,就需要重新定义服务。
并且,客户端的调用方式也发生了变化,需要添加Web引用,通过UDDI去发现服务。
采用WCF则不然。
WCF引入了用通道,它封装了消息的通信细节,例如编码、事务处理、安全等,然后又通过引入绑定的概念,封装了通道的组成顺序与处理细节。
最后,引入了独有的Endpoint元素,集成了地址、绑定和契约之间的“三位一体”,以最简单的方式定义和发布服务。
3.3、WCF基础的技术要素
WCF的大部分功能都放在一个单独的程序集System.ServiceModel.dll中。
WCF的几个最重要的技术元素包括:
绑定、契约、端点。
如前所述,绑定封装了通道的组成顺序与处理细节,它直接决定了WCF的通信方式,消息的编码方式,通道的协议,消息传递的可靠性以及安全等内容。
通过使用绑定,我们就无需了解消息在WCF通道中的实现细节,从而简化程序员的开发。
正是因为此,WCF为开发人员提供了多个内置绑定,基本上涵盖了WCF应用的大部分场景。
以下是AaronSkonnard在《WCF深度绑定》一文中列举的内置绑定:
绑定类名称
传输
消息编码
消息版本
安全模式
可靠消息传送
事务流(默认情况下禁用)
BasicHttpBinding
HTTP
文本
SOAP1.1
无
不支持
不支持
WSHttpBinding
HTTP
文本
SOAP1.2WS-Addressing1.0
消息
禁用
WS-AtomicTransactions
WSDualHttpBinding
HTTP
文本
SOAP1.2WS-Addressing1.0
消息
启用
WS-AtomicTransactions
WSFederationHttpBinding
HTTP
文本
SOAP1.2WS-Addressing1.0
消息
禁用
WS-AtomicTransactions
NetTcpBinding
TCP
二进制
SOAP1.2
传输
禁用
OleTransactions
NetPeerTcpBinding
P2P
二进制
SOAP1.2
传输
不支持
不支持
NetNamedPipesBinding
命名管道
二进制
SOAP1.2
传输
不支持
OleTransactions
NetMsmqBinding
MSMQ
二进制
SOAP1.2
消息
不支持
不支持
MsmqIntegrationBinding
MSMQ
不支持(使用WCF之前的序列化格式)
不支持
传输
不支持
不支持
每种绑定对应不同的传输协议、消息编码格式和版本以及安全、可靠性和事务模式。
WCF也提供了扩展绑定的方式,例如通过CustomBinding或者定义派生与Binding的类。
WCF的契约包括服务契约、数据契约和消息契约(特别的,还包括了错误契约,用于异常的处理)。
其中服务契约为面向服务应用程序的核心,通过它可以定义服务。
数据契约则为服务所要传递的数据。
由于服务的调用需要跨进程或机器进行通信,就需要服务数据必须能够被序列化和反序列化。
虽然.NET本身提供了数据的序列化功能,但WCF的数据契约更加符合服务数据的定义习惯。
至于消息契约,则可以将服务数据定义为消息,包括XML文本格式、MTOM(消息传输优化机制)格式和二进制格式。
绑定(Binding)、契约(Contract)与服务的地址(Address)组合在一起,则形成了终结点(Endpoint),如下图所示:
Address是Endpoint的网络地址,它标记了消息发送的目的地。
Binding描述的是如何发送消息,例如消息发送的传输协议(如TCP,HTTP),安全(如SSL,SOAP消息安全)。
Contract则描述的是消息所包含的内容,以及消息的组织和操作方式,例如是单向,双向还是请求/响应方式。
引入终结点可以说是WCF的一个伟大创举,通过它使得我们能够更加容易的发布和管理服务,尤其是发布和管理多个服务。
每个服务必须至少拥有一个终结点,而客户端正是通过终结点知道服务的相关信息,例如地址、消息编码格式、传输协议以及服务的内容,然后在进行正确的调用。
最特别的是,同一个服务可以定义多个终结点,每个终结点可以是不同的地址、不同的绑定方式,以便于满足多个客户端的不同需要。
而对于服务的发布者而言,我们只需要管理终结点的配置,就可以完成对服务的管理,这也为服务的托管提供了便利。
四原则
4.1封装业务逻辑
所有能脱离界面的业务逻辑的处理都应该封装在WCF服务中
4.2封装数据库访问
数据库的访问应该通过WCF提供的接口访问数据库,不对界面显示层暴露数据库的具体内容
4.3封装文件存储
文件存储应通过WCF提供的接口访问文件服务器进行文件操作
4.4线程安全
WCF服务应该是线程安全的,支持异步调用(涉及并发管理)
4.5数据完整性
Wcf服务应该支持事务,保证数据完整性,所有修改数据库的操作和修改文件的操作都应该有回滚机制(自己实现或使用系统事务)
五实现细则规范
5.1服务契约(接口)
5.1.1服务契约定义
WCF的所有服务都会公开为契约(Contract)。
契约与平台无关,是描述服务功能的标准方式,服务契约描述了客户端能够执行的服务操作。
ServiceContract属性可以将一个CLR接口(或类)映射为与技术无关的服务(WCF契约),WCF契约与类型的访问限定无关,因为类型的访问限定属于CLR的概念。
即使将ServiceContract属性标记在内部接口上,该接口同样会被公开为服务契约,以便跨越服务边界实现服务的调用。
如果接口没有标记ServiceContract属性,WCF客户端则无法访问它。
这一特定遵循了面向服务的一个原则,即服务边界要清晰。
为满足这一原则,所有的契约必须明确要求:
只有接口或类可以被标记为ServiceContract属性,其他类型都不允许。
WCF使用OperationContract属性显著标明接口或类的哪些方法需要暴露为WCF契约的服务操作。
另外WCF只运允许将OperationContract属性标记到方法上,而不允许标记到同样属于CLR概念的属性,索引器和事件上。
WCF只能识别逻辑功能的操作(Operation)。
此外契约操作不能使用对象参数,只允许使用基本类型或者数据契约。
例:
[ServiceContract]
InterfaceIMyContract
{
[OperationContract]
StringMyMethod(stringtext);
//没有[OperationContract]标记,不会成为契约的一部分
StringMyOtherMethod(stringtext)
}
ClassMyService:
ImyContract
{
PublicstringMyMethod(stringtext)
{……
}
PublicstringMyOtherMethod(stringtext)
{……}
}
5.1.2操作重载
5.1.3契约分解
合理的契约分解可以实现深度化,松散耦合,精细调整,以及契约的重用,这些优势有助于改善整个系统。
设计面向服务的系统时,需要平衡两个影响系统的因素(下图),一为实现服务契约的代价,一个则是将服务契约合并或者集成为一个应用程序的代价
如果定义了太多的细粒度的服务契约,虽然他们易于实现,但是集成它们的代价太高,另一方面,如果仅定义了几个复杂而又庞大的服务契约,虽然集成的代价可能会降低,但制约了契约的实现,以及系统集成的灵活性
原则上一个功能模块封装成一个服务契约,对于比较庞大的模块可以按功能分解成两到三个服务契约
5.2数据契约(类)
5.2.1数据契约定义
数据契约定义了与服务交互的数据类型。
面向服务的核心原则就是在跨越服务边界时服务不能够暴露他们的实现技术,这意味着WCF不允许在跨越边界时公开CLR数据类型。
需要找到一种方法,实现CLR数据类型与标准的平台无关的表现形式,这就是基于XML的样式。
事实上,将对象(或值类型)作为操作参数或操作结果传递时,真正需要发送的是对象的状态,然后接收端再将它转换为本地的表现形式。
这种传递状态的方式称为值编组。
执行值编组的最简单办法是利用大多数平台自身提供的序列化技术。
通常在WCF中用DataContract特性来标记类可以被序列化,用DataMember特性标记类的序列化的成员。
例:
[DataContract]
PublicclassContract
{
[DataMember]
PublicstringFirstName
{get;set;}
[DataMember]
PublicstringLastName
{get;set;}
}
5.2.2推断数据契约
wcf有推断数据契约功能,如果编组类型是公共类型,且未标记DataContract特性,WCF就会自动推断,认为DataContract特性被应用到该类型上,且它所有的公有成员均被应用了DataMember特性,依赖于数据契约的推断是一种草率的方法,它与WCF的大多数内容背道而驰。
既然WCF无法从仅有的接口定义中推断出服务契约,或者默认允许事务或可靠性,它就不应该推断数据契约。
面向服务在很大程度上偏向于默认“否决”的方式(安全是一个例外),正因为如此,它需要最大限度的封装与解耦合。
对数据契约显示的使用DataContract特性,就使得你可以利用数据契约的功能,如版本控制,定制序列化成员等。
5.2.3版本控制
服务应尽可能地与它们的客户端解藕,特别涉及版本控制与技术问题时。
任意一个版本的客户端都应该能够调用所有版本的服务。
WCF的版本判断与.NET不同,并不借助版本号。
当服务和客户端共享一个数据契约时,一个重要目标是要允许服务和客户端各自演化数据契约版本。
为支持重要的解藕,WCF支持版本的向前和向后兼容。
5.2.3.1新增成员
数据契约最常见的变化是在其中一端添加了新的成员,然后将新的契约发送到旧的客户端或服务。
在反序列化这样的数据契约类型时,DataContractSerialize会忽略新增成员。
这样,服务和客户端就能够接收包含新增成员的数据,且该新增成员并非原有契约的一部分。
5.2.3.2缺失成员
默认情况下,WCF允许服务与客户端双方都可以移除数据契约的成员。
当接收端的DataContractSerialize在消息中无法找到所需信息去反序列化这些成员时,会根据成员的默认值进行反序列化。
也就是说,把引用类型设置为NULL,将值类型设置为0。
在操作过程中,这就像发送方从来没有初始化这些成员一样。
这种默认策略允许服务接收包含缺失成员的数据,或者能够返回包含了缺失成员的数据到客户端。
5.2.3.3必备成员
与新增成员采取忽略新成员的方式不同,缺失成员的默认处理有可能会导致接收端调用链的操作失败,从而导致运行良好的系统产生灾难性的后果。
其原因在于缺失的成员有可能是正确执行操作的必要条件。
如果成员是缺失的,则可以通过将DataMember特性的IsRequired属性值设置为true来避免WCF调用这些操作而导致调用失败:
[DataContract]
ClassMyContact
{
[DataMember(IsRequired=true)]
PublicstringLastName
{get;set;}
}
IsRequired默认值为false,即忽略缺失成员。
如果消息中的成员被标记为必备成员,当接收端的DataContractSerialize无法找到所需要的信息进行反序列化时,就会取消这次调用,发送端会引发NetDispatcherFaultException异常。
客户端和服务端都能够将他们的部分或所有数据成员标记为必备,彼此之间是完全独立的。
当拥有一个必备新成员的数据契约发送到接收方时,即使接收方无法识别该成员,这样的调用实际上仍然是有效的,同时也允许这样的传递。
5.2.4枚举
枚举类型的定义总是支持序列化的。
当定义一个新的枚举时,不必应用DataContract特性,就可以在数据契约中使用它。
如果要将确定的枚举值排除与数据契约之外,首先就需要为枚举类型标记DataContract属性,然后再将那些希望包含在枚举数据类型中的枚举值,明确的标记为EnumMember特性,没有标记EnumMember特性的枚举值不属于该枚举的数据契约。
EnumMember特性还有一个用途,通过它的Value属性为现有的枚举数据契约的枚举值设置别名。
例:
[DataContract]
EnumContactType
{
//传输时名称为MyCustomer
[EnumMember(Value=”MyCustomer”)]
Customer,
[EnumMember]
Vendor,
//不会成为数据契约的一部分
Partner
}
生成的传输型表示形式如下:
EnumContactType
{
MyCustomer,
Vendor
}
5.2.5委托与事件
所有的委托和事件定义都被编译到可序列化的类中,理论上,委托和事件相当于数据契约类型的成员变量。
如:
[DataContract]
ClassMyDataContract
{
[DataMember]
PublicEventHandlerMyEvent;
[field:
DataMember]
PubliceventEventHandleMyeve;
}
在实际操作中,当数据契约引用一个自定义委托时,导入的数据契约将包含一个无效的委托定义,虽然可以手动修改定义,但更大的问题是在包含了委托成员变量的对象时,委托的内部调用列表也会被序列化。
在大多数情况下,对于服务和客户端而言,这并非理想结果,因为列表具体的结构是本地的,客户端或服务无法跨服务边界共享委托列表的结构。
此外,不能保证内部列表中的目标对象都是可序列化的,或者都是有效的数据契约。
这会导致序列化的操作时而成功,时而失败。
所有,我们约定不要将DataMember特性应用到委托/事件上。
5.2.6泛型
不能定义包含了泛型类型参数的数据契约。
泛型是.NET的特定技术,使用他们可能会与WCF的面向服务特性发生冲突。
但是,仍然可以在数据契约中使用限定的泛型类型,只要在契约中指定了类型参数,并且指定的类型参数具有有效的数据契约,不过这就失去了使用泛型的意义,故不推荐。
5.2.7集合
5.2.7.1.net内建集合
在.net中,各种类型的集合均实现了IEnumerable或IEenumerable
所有内建的.net集合,如数组,列表和栈都实现了这些接口。
一个数据契约的数据成员可以是集合类型,服务契约也可以定义直接与集合交互的操作,因为.net集合是.net特有的,WCF不能在服务元数据中公开他们。
而服务的类型非常有用,所有WCF专门为集合提供了编组原则。
定义服务操作时,不管使用下列那种集合接口IEenumerable
例:
[ServiceContract]
InterfaceIcontactManager
{
[OperationContract]
IEnumerable
}
ClassContactManager:
IcontactManager
{
PublicIEnumerable
{
List
returnconts;
}
……
}
导出的结果为:
[ServiceContract]
InterfaceIcontactManager
{
[OperationContract]
Contact[]GetContacts();
}
5.2.7.2具体集合类型
如果契约中的集合为具体集合类型(不是接口),而且属于可序列化集合(标记为Serializable特性而不是DataContract特性),那么,只要提供的集合包含了Add()方法,并符合如下的一种方法签名,WCF就能够自动地将集合规范为数组类型。
PublicvoidAdd(objectobj);//CollectionusesIEnumerable
PublicvoidAdd(Titem);//CollectionusesIEnumerable
例:
//////////////////////////////////////服务端//////////////////
[ServiceContract]
publicinterfaceIService
{
[OperationContract]
List
}
publicclassService:
IService
{
publicList
{
MyClassm=newMyClass();
List
list.Add(m);
returnlist;
}
}
/////////////////////////客户端///////////////////
publicinterfaceIService
{
WCFClient.Service.MyClass[]GetMyClasss();
}
publicpartialclassServiceClient:
System.ServiceModel.ClientBase
{
publicWCFClient.Service.MyClass[]GetMyClasss()
{
returnbase.Channel.GetMyClasss();
}
}
注意:
为了能够将它编组为数组,集合必须包含Add()方法,但并不需要实现Add()方法。
5.2.7.3自定义集合
并非只有内建的集合类型才具有自动编组为数组的能力,任何自定义集合只要符合相同的先决条件(包含Add()方法),就可以被编组为数组。
5.2.7.4字典集合
字典集合是一个特殊的集合类型,它可以为两个数据契约类型建立映射关系,因此,字典集合不会被编组为数组或列表。
如果字典集合是一个实现了IDdictionary接口的可序列化集合,那么它会被公开为Dictionary
例:
[Serializable]
PublicclassMyDictionary:
IDdictionary
{……}
[ServiceContract]
InterfaceIcontactManager
{
…
[OperationContract]
MyDictionaryGetContacts();
[OperationContract]
Dictionary
}
公开的定义为:
[ServiceContract]
InterfaceIcontactManager
{
…
[OperationContract]
Dictionary
[OperationContract]
Dictionary
}
5.2.7.5Colle
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WCF 服务 编程 规范