一步一个脚印学习WCF系列之WCF契约设计—1-服务契约ServiceContractAttribute
阅读目录
一:SOA体系结构
二:WCF当中的客户端与服务
三:服务契约
四:实例
一:SOA体系结构
. 我在这里用图形在回顾一下,首先我们来看SOA的体系结构,在我们传统的经典的三层体系结构有客户层,业务逻辑层,数据访问层,而在SOA体系结构中多了一层服务层,就客户层而言它是与服务层进行通信的,然后再由服务层把客户所要求的服务拆解为相应的多个不同的业务逻辑,传到业务逻辑层进行处理。就SOA这种面向服务的体系结构中重要的特点就是客户层和业务逻辑层存在了一个服务层,客户层在进行服务调用的时候并不是直接与业务逻辑层进行通信,而是通过服务层完成他的需求
二:WCF当中的客户端与服务
. 在宿主进程嵌入了一个或者多个服务,服务通过EndPoint也就是端点对外进行暴露,在客户端进程如果想获取宿主进程当中的服务,需要一个Proxy也就是代理,客户端进程如果说现在需要获取宿主进程当中的某一个服务的话,客户端把要求提交给代理,然后通过代理封装到EndPoint当中,再通过Message也就是消息的方式传到宿主进程当中的EndPoint,在提交到Service当中去处理,处理完毕在经过一个反向的过程。ABC-A-Address 服务器端的地址,B-Binding 服务器端绑定了哪些通讯的协议,比如是TCP还是HTTP协议,C-Contract 对于客户端和服务器端而言要进行消息的通讯为了保证能够正常的处理消息,它们之间对传输的消息要有一个约定,这个约定我们称之为契约。对于Proxy而言它是客户端的一部分,它作为一个类存在于客户端当中的,客户端通过Proxy来访问宿主进程当中的Service服务或者说Proxy它帮客户端屏蔽掉了在宿主进程Server当中具体细节,这样的话通过Proxy在客户这一端像类似于本地调用方式实现远程或者分布式服务的调用
三:服务契约
. 在WCF当中服务契约是通过属性的方式来定义的,也就是我们希望在服务这一端暴露服务给客户端,我们可以在这些服务上面我们通过添加ServiceContract和OperationContract这样的属性,在以前我们开发WebService的时候要想给外界暴露一些Web方法的话需要添加WebMethod标记,ServiceContract和OperationContract的添加和WebMethod的添加是类似的效果
1:ServiceContractAttribute
. 应用于接口或者类中
建议在接口上定义服务契约,因为接口本身并不包含具体的业务逻辑,因此它可以实现这个服务和于这个服务具体实现的松耦合性,如果把服务契约定义在public class ContentManagerService这个类当中的话,如果现在这个类里面的具体业务逻辑发生了变化,实际上也就意味着这个契约也就发生了变化,在WCF当中服务这一端和客户这一端是共享契约的,就会导致客户这一端的契约也会发生变化,直接导致的结果是服务端具体业务逻辑所发生的变化会给客户端的契约带来相应的影响,对于服务这一端和客户这一端耦合度得到了提高。如果把契约定义在接口上,那么就算public class ContentManagerService类发生了变化,由于接口没有发生变化,所以客户这一端绑定服务契约的接口也没有发生变化,对我们的客户端不会带来影响
. 建议应用于接口当中
很多情况下我们的这个服务的业务逻辑比较复杂,这个服务可能会有多个契约,假设我们把服务契约定义在public class ContentManagerService类上,在C#当中类与类的继承是一个单继承的关系,public class NewContentManagerService这个类比较复杂要使用到多个契约,不但继承ContentManagerService类还要继承其他的类,那么它只能继承一个类,所以只能使用一个契约。我们把服务契约定义在接口上面,由于接口是多继承的关系,我们的新的这个类public class NewContentManagerService继承两个都具有服务契约的接口,提高了服务契约的灵活性
. 总是提供有意义的命名空间
1 Namespace = "http://www.menglin.net/"
. 能够显示的指定Name
这个名称作用是服务契约所描述的接口暴露给客户端的名称,在客户端不使用IHelloWorldService来对接口进行操作使用,而是使用指定的名称HelloWorldServiceContract对接口进行操作,解除了接口名称和服务契约名称的耦合性,这种耦合性体现在具体语言接口名称和通用XML名称的耦合性上
1 [ServiceContract(Name = "HelloWorldServiceContract", Namespace = "http://www.menglin.net/")] 2 public interface IHelloWorldService 3 { 4 [OperationContract] 5 string GetHelloWorld(); 6 }
2:OperationContractAttribute
. 服务契约中的所有方法都应该具有OperationContractAttribute
1 [OperationContract] 2 string GetHelloWorld();
. 能够显示的指定Name,Action,ReplyAction
3:MessageParameterAttribute
. 我们不仅能使用ServiceContract定义接口和类,使用OperationContract定义具体的方法,还能使用MessageParameter定义具体的参数,我们把SaveUser声明为一个暴露出的服务,并且在SaveUser当中,user这个参数接收来自UserInfo当中的具体的Message,Name="UserInfo“和我们定义的User user是一个什么关系呢?在SOA架构当中客户端与服务器端之间消息的传递是通过SOAP协议进行传递的,SQAP协议本身是通过XML这种数据表示方式来进行描述的,在XML当中包含了许多不同标记,比如:User user在XML当中是通过<UserInfo.../>来描述SOAP里面具体的信息,无论怎么描述,在SOAP当中保存着客户端和服务器端之间传递的具体数据,对于SOA架构而言的话,在SOA设计之初,本身的设想是跨平台的,跨不同技术的架构,在SOAP里包含的数据可能是不同的具体技术或者不同的平台来生成的数据,也许是由WCF生成的,也可能是由Java平台上具体的技术来生成遵循SOAP协议的具体消息,从二进制的兼容性而言.NET平台和Java平台是不兼容的,为了让.NET平台和Java平台的客户端或者服务端之间能够相互通信,实际上通过SOAP协议来进行数据的通信,如何来进行通信呢?就用到了SOAP协议当中遵循XML格式的这些具体的标记,比如:现在有个Java开发的客户端Java Client,它通过java的代码发送了一个消息,这个消息里面包含一些<UserInfo.../>的具体标记,Java开发的客户端访问由WCF开发的服务,当这个消息发送给这个WCF服务的时候,WCF服务会检查由Java Client发送过来的这个SOAP消息,里面有没有UserInfo,如果有的话就会对应到([MessageParameter(Name="UserInfo")],就会把UserInfo里面的数据通过反序列的方式把它传递给User下的user,通过这种方式就实现了由Java开发的客户端到WCF开发的服务之间对象的传递,这种对象传递之间的映射关系就是通过MessageParameterAttribute来进行映射的
. MessageParameterAttribute主要作用,就是一句话完成从SOAP消息到具体对象之间的映射关系
1 [OperationContract] 2 void SaveUser([MessageParameter(Name="UserInfo")] User user)
四:实例
1:GradeService
1.1:IGradeService.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 namespace GradeService 8 { 9 /// <summary> 10 /// 定义一个由服务契约来描述的接口 11 /// </summary> 12 [ServiceContract(Name = "StudentGradeService", Namespace = "http://www.menglin.net/")] 13 public interface IGradeService 14 { 15 /// <summary> 16 /// 得到张三的成绩 17 /// </summary> 18 /// <returns></returns> 19 [OperationContract(Name="SelectZhangSanGrade")] 20 string GetZhangSanGrade(); 21 22 /// <summary> 23 /// 得到李四的成绩 24 /// </summary> 25 /// <returns></returns> 26 [OperationContract(Name = "SelectLiSiGrade")] 27 string GetLiSiGrade(); 28 } 29 }
1.2:GradeService.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 namespace GradeService 8 { 9 /// <summary> 10 /// 具体的服务实现了服务契约 11 /// </summary> 12 public class GradeService:IGradeService 13 { 14 /// <summary> 15 /// 得到张三的成绩 16 /// </summary> 17 /// <returns></returns> 18 public string GetZhangSanGrade() 19 { 20 return "80"; 21 } 22 23 /// <summary> 24 /// 得到李四的成绩 25 /// </summary> 26 /// <returns></returns> 27 public string GetLiSiGrade() 28 { 29 return "90"; 30 } 31 } 32 }
2:Host
2.1:Program.cs
1 static void Main(string[] args) 2 { 3 ServiceHost host = null; 4 try 5 { 6 host = new ServiceHost(typeof(GradeService.GradeService)); 7 host.Open(); 8 Console.ReadLine(); 9 } 10 finally 11 { 12 host.Close(); 13 } 14 }
2.2:App.config
关于Host端的配置信息我在《一步一个脚印学习WCF系列之WCF概要—WCF服务的创建与调用HelloWorld实例,通过配置文件方式(六)》一文中已经讲得很详细了,如有不明白的请参照此文
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <services> 5 <service name="GradeService.GradeService" behaviorConfiguration="serviceBehavior"> 6 <host> 7 <baseAddresses> 8 <add baseAddress="http://localhost:8230"/> 9 </baseAddresses> 10 </host> 11 <endpoint address="GradeService" 12 binding="basicHttpBinding" 13 contract="GradeService.IGradeService" /> 14
15 <endpoint address="mex" 16 binding="mexHttpBinding"
17 contract="IMetadataExchange" /> 18
19 </service> 20 </services> 21 <behaviors> 22 <serviceBehaviors> 23 <behavior name="serviceBehavior"> 24 <serviceMetadata httpGetEnabled="true" /> 25 </behavior> 26 </serviceBehaviors> 27 </behaviors> 28 </system.serviceModel> 29 </configuration>
3:Client
3.1:Form1.cs
1 private void btnZhangSan_Click(object sender, EventArgs e) 2 { 3 Proxy.StudentGradeServiceClient proxy = new Proxy.StudentGradeServiceClient(); 4 string Result = proxy.SelectZhangSanGrade(); 5 MessageBox.Show(Result); 6 } 7 8 private void btnLiSi_Click(object sender, EventArgs e) 9 { 10 Proxy.StudentGradeServiceClient proxy = new Proxy.StudentGradeServiceClient(); 11 string Result = proxy.SelectLiSiGrade(); 12 MessageBox.Show(Result); 13 }
运行效果图