所以WCF下的序列化解决的就是如何将数据从对象的变现形式转变成WCF 的序列化和反序列化解决的是托管对象与
数据契约
一个正常的服务调用要求客户端和服务端对服务操作有一致的理解,WCF通过服务契约对服务操作进行抽象,以一种与平台无关的,能够被不同的厂商理解的方式对服务进行描述。同理,客户端和服务端进行有效的数据交换,同样要求交换双方对交换数据的结构达成共识。WCF通过数据契约来对交换的数据进行描述。与数据契约的定义相匹配,WCF采用新的序列化器---数据契约序列化器(DataContractSerializer)进行基于数据契约的序列化与反序列化.数据契约是描述数据的结构,数据交换只有在双方对数据结构具有相同的理解下才能正常进行。对于数据的接受方来讲,当它接收到数据时,只有借助于数据结构的描述,才能理解数据的每个元素所承载的信息。由于数据交换通过
数据契约的定义
同服务契约类似,WCF采用了基于特性的数据契约定义方式。基于数据契约的自定义特性主要包含以下两个DataContractAttribute和DataMemberAttribute。
1.DataContractAttribute:将目标类型定义成一个数据契约
2.DataMemberAttribute:将字段或属性变成数据契约的数据成员
接下来 我们做个关于 数据契约的 小Demo, 网盘 自己找
首先我们看下 项目的结构
[ 6-01 ]
ContentTypes:数据契约层
GigEntry:客户端
GigManager:服务契约层 ( 这里 把服务契约的 实现 也写到了 这里。当然 也可以把它分开 )
Hosting:服务器寄宿
在看下我们的 服务契约:
[ 6-02 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using ContentTypes; 7 8 namespace GigManager 9 {10 11 [ServiceContract(Name = "GigManagerServiceContract", Namespace = "http://www.HulkXu.com/")]12 public interface IGigManagerService13 {14 //注意 如果不实用数据契约,无法传入复杂的对象,就如下面的 SaveGig 方法的 参数 是一个 ListItem 类型的参数15 [OperationContract]16 void SaveGig(ListItem item);17 [OperationContract]18 ListItem GetGig();19 }20 21 /// <summary>22 /// 这里为实现了 IGigManagerService 这个服务契约23 /// </summary>24 public class GigManagerService : IGigManagerService25 {26 private ListItem m_listItem;27 /// <summary>28 /// 将我们得到的 对象传给 m_listItem29 /// </summary>30 /// <param name="item"></param>31 public void SaveGig(ListItem item)32 {33 m_listItem = item;34 }35 36 /// <summary>37 /// 获取我们上面的 得到的 m_listItem 38 /// </summary>39 /// <returns></returns>40 public ListItem GetGig()41 {42 return m_listItem;43 }44 }45 }
注意看 上面的 ListItem 参数,这个参数是一个复杂类型的参数,因为是我们自己定义的参数。
在来看我们的 服务器寄宿:
代码 ( 没有什么变化 )
[ 6-03 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 namespace Hosting 8 { 9 class Program10 {11 static void Main(string[] args)12 {13 using (ServiceHost host = new ServiceHost(typeof(GigManager.GigManagerService)))14 {15 host.Open();16 Console.WriteLine();17 Console.WriteLine("服务已经启动");18 Console.ReadLine();19 }20 }21 }22 }
Appconfig的配置
[ 6-04 ]
1 <?"1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <services> 5 <service name="GigManager.GigManagerService" behaviorConfiguration="serviceBehavior"> 6 <!--配置一个TCP协议的相对地址--> 7 <endpoint address="GigManagerService" contract="GigManager.IGigManagerService" binding="netTcpBinding"></endpoint> 8 <!--这里是一个固定的写法,用于发布TCP协议的 服务契约 (使用HTTP协议的方式展现出来)--> 9 <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"></endpoint>10 <host>11 <baseAddresses>12 <!--WSDL呈现地址-->13 <add baseAddress="http://127.0.0.1:8000"/>14 <add baseAddress="net.tcp://127.0.0.1:9000"/>15 </baseAddresses>16 </host>17 </service>18 </services>19 <behaviors>20 <serviceBehaviors>21 <behavior name="serviceBehavior">22 <serviceMetadata httpGetEnabled="true"/>23 </behavior>24 </serviceBehaviors>25 </behaviors>26 </system.serviceModel>27 </configuration>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"></endpoint> 这个节点呢 是 用于我们 在浏览器中 去查看 元数据(WSDL) 的,注意它是一个固定的写法,也就是看服务契约以WSDL形式发布出来的呈现
它通过我们配置的地址来访问
<add baseAddress="http://127.0.0.1:8000"/>
为什么这么做?因为TCP协议的 方式 你怎么查看 WSDL的文档,只有通过 HTTP方式 在浏览器上 去查看。
再接下来看我们的 数据契约了:
之前我们看到 服务契约 的 SaveGig 方法,需要传入 一个 复杂类型 ListItem,而这个复杂类型就是我们自己定义的类。
这里我们需要将这个复杂类型变成我们的 数据契约
再之前我们通过查看描述数据契约的WSDL文档中的XSD(定义WSDL的文档)发现,每一个数据成员的命名习惯同类型定义中的属性相同。而且,序列中的每一个元素的顺序是字母顺序,与其在类型定义中出现的顺序相反。另一件值得注意的事
情就是,该类型的命名空间同服务契约目标命名空间不匹配。 它使用的是shemas.datacontract.org
这些都可以在数据契约中解决:
DataMember:相应的属性
Name:控制生成的模式元素名称。默认时使用CLR中定义的名称。
IsRequired:为模式元素控制minOccurs属性。默认时所有的元素都是可选的(minOccurs=0)
Order:控制模式中每一个元素的次序。默认时无序的数据成员按照字母顺序排序。
[ 6-05 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.Serialization; 6 7 namespace ContentTypes 8 { 9 10 /// <summary>11 /// 这里主要是WCF 序列化 该类12 /// 这么想,客户端直接引用 ContentTypes 层 然后 直接 调用 服务端的方法传递 这个复杂的参数(ListItem) ,WCF 会根据这里定义好的数据契约 直接序列化成对应的13 /// </summary>14 [DataContract(Namespace = "http://www.HulkXu.com/")]15 //[DataContract]16 public class ListItem17 {18 [DataMember(Name = "Id", IsRequired = false, Order = 0)]19 //[DataMember]20 private long m_id;21 22 public long Id23 {24 get { return m_id; }25 set { m_id = value; }26 }27 //[DataMember]28 [DataMember(Name = "Title", IsRequired = true, Order = 1)]29 private string m_title;30 31 public string Title32 {33 get { return m_title; }34 set { m_title = value; }35 }36 //[DataMember]37 [DataMember(Name = "Description", IsRequired = true, Order = 2)]38 private string m_description;39 40 public string Description41 {42 get { return m_description; }43 set { m_description = value; }44 }45 //[DataMember]46 [DataMember(Name = "DateStart", IsRequired = true, Order = 3)]47 private DateTime m_dateStart;48 49 public DateTime DateStart50 {51 get { return m_dateStart; }52 set { m_dateStart = value; }53 }54 //[DataMember]55 [DataMember(Name = "DateEnd", IsRequired = false, Order = 4)]56 private DateTime m_dateEnd;57 58 public DateTime DateEnd59 {60 get { return m_dateEnd; }61 set { m_dateEnd = value; }62 }63 //[DataMember]64 [DataMember(Name = "Url", IsRequired = false, Order = 5)]65 private string m_url;66 67 public string Url68 {69 get { return m_url; }70 set { m_url = value; }71 }72 73 }74 }
最后我们看我们的客户端:
客户端是一个 Form 的 应用程序
界面
[ 6-06 ]
代码:
[ 6-07 ]
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using GigEntry.localhost;10 11 namespace GigEntry12 {13 public partial class Form1 : Form14 {15 //创建代理对象16 localhost.GigManagerServiceContractClient proxy = new GigManagerServiceContractClient();17 18 public Form1()19 {20 InitializeComponent();21 22 }23 24 /// <summary>25 /// 保存 26 /// </summary>27 /// <param name="sender"></param>28 /// <param name="e"></param>29 private void cmdSave_Click(object sender, EventArgs e)30 {31 //这里WCF不再需要序列化 ListItem 这个参数了,因为 在 ContentTypes 层的 ListItem.cs 类中。我们已经定义好 这个类的序列化规则了32 ListItem item = new ListItem();33 item.Id = int.Parse(this.txtId.Text);34 item.Title = this.txtTitle.Text;35 item.Description = this.txtDescription.Text;36 item.DateStart = this.dtpStart.Value;37 item.DateEnd = this.dtpEnd.Value;38 item.Url = this.txtUrl.Text;39 proxy.SaveGig(item);40 }41 42 /// <summary>43 /// 获取44 /// </summary>45 /// <param name="sender"></param>46 /// <param name="e"></param>47 private void cmdGet_Click(object sender, EventArgs e)48 {49 ListItem item = proxy.GetGig();50 if (item != null)51 {52 this.txtId.Text = item.Id.ToString();53 this.txtTitle.Text = item.Title;54 this.txtDescription.Text = item.Description;55 this.dtpStart.Value = item.DateStart;56 this.dtpEnd.Value = item.DateEnd;57 this.txtUrl.Text = item.Url;58 59 }60 }61 62 63 }64 }
appconfig 配置:
这里的配置 是我们 添加了 服务引用 自动生成 的 ,可以不管
[ 06-08 ]
1 <??> 2 <configuration> 3 <system.serviceModel> 4 <bindings> 5 <netTcpBinding> 6 <binding name="NetTcpBinding_GigManagerServiceContract" closeTimeout="00:01:00" 7 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 8 transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 9 hostNameComparisonMode="StrongWildcard" listenBacklog="10"10 maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"11 maxReceivedMessageSize="65536">12 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"13 maxBytesPerRead="4096" maxNameTableCharCount="16384" />14 <reliableSession ordered="true" inactivityTimeout="00:10:00"15 enabled="false" />16 <security mode="Transport">17 <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />18 <message clientCredentialType="Windows" />19 </security>20 </binding>21 </netTcpBinding>22 </bindings>23 <client>24 <endpoint address="net.tcp://127.0.0.1:9000/GigManagerService"25 binding="netTcpBinding" bindingConfiguration="NetTcpBinding_GigManagerServiceContract"26 contract="localhost.GigManagerServiceContract" name="NetTcpBinding_GigManagerServiceContract">27 <identity>28 <userPrincipalName value="20080904-1145\Administrator" />29 </identity>30 </endpoint>31 </client>32 </system.serviceModel>33 </configuration>
运行程序后 Hosting 服务器寄宿 然后在浏览器中 输入 http://127.0.0.1:8000 我们配置好的 WSDL呈现地址
[ 06-09 ]
[ 06-10 ]
[ 06-11 ]
[ 06-12 ]
看 06-12 图,我们很清楚的看到了 将 ListItem 类型 序列化成
根据上面的 代码 ,我们将 数据契约 ListItem类的成员上面的 特性 [DataMember(Name = "Id", IsRequired = false, Order = 0)] 都去掉 ,换成 [DataMember]
[ 6-13 ]
再次 根据 运行 Hosting 服务器寄宿 然后在浏览器中 输入 http://127.0.0.1:8000 从图片 06-09 开始 到 06-12 的操作步奏 进行一次。
仔细观察 与之前 的 是否不太一样?
例如 这里的名字?
[ 6-14 ]
例如 这些字段的排序顺序?
[ 6-15 ]
等等,这里我就不贴 测试的图片了。自己下去试一试。
原标题:第六讲:数据契约
关键词: