上篇博客我们谈到了契约,今天我们从契约开始。那么什么是契约呢?在使用WCF时,对其制定各种各样的规则,就叫做WCF契约。任何一个分布式的应用程序在传递消息的时候都需要实现制定一个规则。
任何一个分布式应用程序,它之所以能够互相传递消息,都是事先制定好数据交换规则的,这个规则正是交换数据的双方(比如服务器端和客户端)能彼此理解对方的依据,WCF作为分布式开发技术的一种,同样具有这样一种特性。而在WCF中制定的的规则就被称之为契约(Contract),它是WCF的消息标准,是任何一个wcf程序不可或缺的一部分。
而契约可以分为四种:服务契约、数据契约、消息契约和错误契约。
这种级别的契约又包括两种:ServiceContract和OperationContract。ServiceContract用于类或者结构上,用于指示WCF此类或者结构能够被远程调用,而OperationContract用于类中的方法(Method)上,用于指示WCF该方法可被远程调用。
所以实现“服务契约”很容易:定义一个接口,给接口附加[ServiceContract],给接口方法附加[OperationContract]。
///<summary> ///为考试提供服务 ///</summary> [ServiceContract] public interface IExamEntityService { #region 添加考试——王金博——2014-12-19 16:09:12 ///<summary> ///添加考试 ///</summary> ///<paramname="enExam">考试实体</param> ///<returns>考试实体</returns> [OperationContract] bool AddExam(ExamExamEntity enExam); #endregion
一个类可以实现多个[ServiceContract]。服务契约描述了暴露给外部的类型(接口或类)、服务所支持的操作、使用的消息交换模式和消息的格式。每个WCF服务必须实现至少一个服务契约。
数据契约也分为两种:DataContract和DataMember.DataContract用于类或者结构上,指示 WCF此类或者结构能够被序列化并传输,而DataMember只能用在类或者结构的属性(Property)或者字段(Field)上,指示WCF该属性或者字段能够被序列化传输。
数据契约定义将在客户端与服务器端传送的各种数据类型。
[Classes("成绩管理类")] [DataContract] public class ResultManageEntity { #region 卷面成绩单 [DataMember] private string papeReportCardId; [Colum("卷面成绩ID", DbType= DbType.String)] public string PapeReportCardId { get { return papeReportCardId; } set { papeReportCardId = value; } } [DataMember] private Nullable<decimal>totalScore; [Colum("总成绩", DbType= DbType.String)] public Nullable<decimal>TotalScore { get { return totalScore; } set { totalScore = value; } }
其实利用数据契约已经能够很好地完成数据的传输了,而数据契约只能控制消息体,有时候我们想在数据传递过程中添加一些额外信息,而不希望添加额外的契约字段,那么我们就得改消息报头,也就是说该使用消息契约了。
和数据契约不同的是消息契约更多的是关注数据成员在SOAP消息中的表示。
[MessageContract] public class MessageTest { [MessageHeader] public int Age { get; set; } [MessageHeader] public string Name { get; set; } [MessageBodyMember] public string Email { get; set; } }
接下来换另一个话题——异常处理。和普通服务器编程一样,在WCF的服务端也是会引发异常的,比如在服务器端除了一个0,这时候异常会抛出到服务器端,那么既然WCF是分布式通信框架,就需要把异常信息发送给调用它的客户端。
如果把异常的堆栈信息直接发送给客户端,显然是非常危险的(不解释),所以经过WCF的内部处理,只会在客户端抛出“由于内部错误,服务器无法处理该请求”的异常信息。
try { object returnValue =methodCall.MethodBase.Invoke(channel, copiedArgs); methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length,methodCall.LogicalCallContext, methodCall); ((ICommunicationObject)channel).Close(); } catch (TargetInvocationException tiEx) { string title; if (tiEx.InnerException is NotSupportedException) { title = string.Concat("WCFExpetion", "NotSupportedException", "WCF服务契约异常"); throw new Exception( string.Format("{0}:\n{1}", title, tiEx.InnerException.Message), tiEx.InnerException); } else if (tiEx.InnerException is FaultException) { title = string.Concat("WCFExpetion", "FaultException", "方法内部出现没有处理的异常"); throw new Exception(string.Format("{0}:\n{1}", title, tiEx.InnerException.Message), tiEx.InnerException); } else if (tiEx.InnerException is CommunicationException) { title = string.Concat("WCFExpetion", "CommunicationException", "网络异常,请检查地址是否正确"); throw new Exception( string.Format("{0}:\n{1}", title, tiEx.InnerException.Message), tiEx.InnerException); } else if (tiEx.InnerException is TimeoutException) { title = string.Concat("WCFExpetion", "TimeoutException", "连接超时,请检查网络是否正常"); throw new Exception( string.Format("{0}:\n{1}", title, tiEx.InnerException.Message), tiEx.InnerException); } } catch (Exception ex) { throw new Exception(string.Format("WCF出现未知异常:\n{0}", ex.Message), ex); } finally { ((ICommunicationObject)channel).Abort(); } return methodReturn;
似乎契约使用起来应该很难的样子了,可事实上,契约的使用是很简单的,它无非就是在普通的程序结构上添加一些声明性的属性就可以了.
WCF作为一种能够跨平台的体系框架,其应用肯定会有异构,异网的情况发生,那么作为通讯依据的契约能否自动适用于上述情况呢?答案是肯定的,契约是独立于平台之外的,它只约束通讯的双方应该遵守什么样的规则,而丝毫不管双方各自采用的是什么样的技术和什么样的操作系统,也只有这样,WCF才能有真正的生命力。