一起学WCF【5】

来源:互联网 发布:淘宝开女装店去哪进货 编辑:程序博客网 时间:2024/05/01 07:55

通过上面的示例我们对wcf服务有了一个直观的了解并使用了ServiceContract和OperationContract特性。下面看看数据契约。

数据契约

传递的数据取决于传输参数和返回值类型。参数的值作为内存中的.NET对象被转换为一个对应的格式并嵌入到SOAP消息中。另一方面,从SOAP消息中提取参数,并以.NET对象的形式进行提供。这个转换过程由专门的序列化类实现。

WCF以DataContractSerializer类作为标准的序列化器,对对象进行序列化和反序列化。如果软件开发超出该序列器功能,再使用传统的XML序列化。

DataContractSerializer支持多种数据类型,有:基本数据类型、带有[DataContract]特性的数据类型、标记为可序列化的类、实现IXmlSerializable接口的类、枚举、集合和泛型集合。

前面的示例因为是基本数据类型,本身就是可序列化的,因此没有用到序列化器。当方法的参数或返回值使用复杂的数据类型时,就必须使用合适的特性来决定它们的序列化方法。

[DataContract]和[DataMember]来自System.Runtime.Serialization名称空间,用来指示序列化器如何序列化一个特殊类型的数据。由于这个交换数据结构属于契约的成员,因此使用它们会直接影响已准备好的WSDL文档,数据的定义以XSD格式插入WSDL里。

如果在操作中使用了复杂的数据类型,但是没有指示DataContractSerializer类如何对它序列化,那么所有公开可见的成员都可自动序列化。利用这个特性可以序列化POCO对象而不对程序进行修改。也可用[IgnoreDateMember]特性把某些成员排除在序列化之外。

 

一个例子

依照以前的例子,我们构建一个简单的系统。解决方案名为CarRental,顾名思义是关于汽车租赁的。经过定义接口,实现服务,IIS寄宿服务和实现客户端。

初始的代码如下:

接口:

using System;

using System.ServiceModel;

 

namespace CarRentalService.Interface

{

   [ServiceContract]

    publicinterface ICarRentalService

    {

       [OperationContract]

        doubleCalculatePrice(DateTime pickupData, DateTime returnDate, string pickupLocation,string vechilePreference);

    }

}

实现:

using System;

using CarRentalService.Interface;

 

namespace CarRentalService

{

    publicclass CarRentalService : ICarRentalService

    {

        publicdouble CalculatePrice(DateTime pickupDate, DateTime returnDate, stringpickupLocation, string vehiclePreference)

        {

           Random r = new Random(DateTime.Now.Millisecond);

           return r.NextDouble() * 500;

        }

    }

}

 

选择寄宿IIS。写svc文件,放在CarRentalService项目根目录下:

<%@ ServiceHostService="CarRentalService.CarRentalService" %>

并添加Web.config。内容同上例,可是做些小修改。

在IIS上部署。

访问svc文件,效果如下图:

 

本例中我们用到的是简单数据类型,DataContractSerializer类可直接作用于它,无需事先采取序列化措施。然而对于大多数情形,必须要考虑交换数据的结构,因此必须通过契约的形式通知使用者,这样可以使用[DataContract]和[DataMember]特性指示DataContractSerializer序列化器如何进行序列化,把对象序列化为任一格式。

如果在操作中使用了复杂的数据类型,但是没有指示DataContractSerizlizer类如何对它们进行序列化,那么所有公开的成员都可自动序列化。利用这个特性可以序列化POCO对象而不必对程序进行修改。

修改上例,将CalculatePrice方法的输入参数类型改为PriceCalculationRequest,返回值改为PriceCalculationResponse。

在CarRentalService.Interface中定义这PriceCalculationRequest类如下。记得添加usingSystem.Runtime.Serialization; 和using System.Xml.Serialization;。

 

publicclass PriceCalculationRequest

        {

            public DateTime PickupDate { get;set; }

            public DateTime ReturnDate { get;set; }

            public string PickupLocation { get;set; }

            public string ReturnLocation { get;set; }

            private string VehicleType { get;set; }

            [IgnoreDataMember]

            public string Color { get; set; }

        }

这里我们使用了[IgnoreDataMember]特性。所有成员都进行了序列化除了Color。

但是只有在极少数情况下才把包含所有公开属性在内的事务对象公开为标准对象。在SOA中,必须显示定义契约,必须获得对自定义元素的名称空间的控制,控制每个特性发生的顺序以及它们是否是必须的。

DataContractSerializer类利用[DataContract]和[DataMember]这两个特性来控制序列化过程。[DataContract]类属于单向确认模型,即只有显式设置为[DataMember]特性的成员变量或属性才可以被序列化。修改上述代码如下:

[DataContract]

        public class PriceCalculationRequest

        {

            [DataMember]

            public DateTime PickupDate { get;set; }

            [DataMember]

            public DateTime ReturnDate { get;set; }

            [DataMember]

            public string PickupLocation { get;set; }

            [DataMember]

            public string ReturnLocation { get;set; }

            [DataMember]

            private string VehicleType { get;set; }           

            public string Color { get; set; }

        }

查看生成的XSD部分文档,关于它的模式有如下特性:

特性按字母顺序排列;minOccurs特性的值为0,表示该特性可选;基本CLR数据类型被转换成XSD格式。

 

数据契约详解

从上例中看出,自定义的数据类型通常被赋予[DataContract]特性,自定义的单个变量则被赋予[DataMember]特性。

如果把上述定义改成如下:

[DataContract(Name="PriceCalculationReques",Namespace="http://schemas.datacontract.org/2004/07/CarRentalService.Interface")]

    public class PriceCalculationRequest

    {

       [DataMember(Name="PickupDate", Order=1, IsRequired=true)]

        public DateTime PickupDate { get; set;}

       [DataMember(Name="ReturnDate", Order=3)]

        public DateTime ReturnDate { get; set;}

        [DataMember(Order=2)]

        public string PickupLocation { get;set; }

        [DataMember(Order=4)]

        public string ReturnLocation { get; set;}

        [DataMember]

        private string VehicleType { get; set;}

        public string Color { get; set; }

}

如果分析XSD文档中新创建的模式,它与原来的模式兼容。

在使用System.Runtime.Serialization名称空间中的[DataContract]和[DataMember]特性时,需要遵守以下原则:

默认的名称空间可以由namespace特性改写;[DataContract]属性用来定义XSD中复杂类型的名称;可以忽略成员变量的访问修饰符;只有用[DataMember]特性显示说明的成员变量才参加序列化;IsRequird属性为True,如果在反序列化时找不到这个特性就会产生错误;用Order属性可以确定特性的顺序;[DataMember]特性的name属性用来决定XSD元素的名称。

[DataContrac]和[DataMember]特性的属性在版本控制和互操作情形下起着决定作用。

 

KnownTypes特性

在使用面向对象编程技术时,经常需要引用的是派生类而不是基类。在WCF中,多态性如果不采取预防措施,就容易出错。

继承类的定义没有包含在WSDL中。即为代理代码生成的继承类是未知的,因此不可被反序列化。再如,当引用的某个参数属于非泛型集合类时,也会生成同样的错误。

使用KnownType特性可以解决这个问题,它可以把继承的数据类型添加到数据契约中,从而正确实现序列化和反序列化。

因此必须在数据类中使用[KnowType]特性,或者在服务契约或操作契约的定义中使用[ServiceKnowType]特性。

[KnowType]的一个简单用法是,在基类的数据契约中列出所有的派生类。该特性指示DataContractSerializer类,除了基类,还要在数据契约中插入全部派生类,继承关系保留在XSD中。

如果只是想在某些方法中引用派生类的一个实例,或是想一个服务契约的全部操作可以使用一个继承类,则可在操作级或服务级使用[ServiceKnownType]特性。

如果要公开一个更加灵活的KnowType类型变量,需要将此信息添加到配置文件的system.runtime.serialization节点中。

判断WCF以前的哪些类型(KnowType、ServiceKnowType、config)支持派生类并不容易。每次派生一个新类,就必须给基本契约另外添加一个[KnowType]特性。为此,WCF提供公开KnowType类型的其他办法。它提供一个方法名,此方法返回一个派生类列表。[KnowType]和[ServiceKnowType]都可以实现此功能。


原创粉丝点击