传说中的WCF(5):数据协定(a)

在第4篇中,咱们吹了发送/接收SOAP头,从本篇开头,我们不妨更深入地去探求一下有关WCF中的消息到底是啥玩意儿。WCF庞大而复杂,而从MSDN文档中,你会看到许多很专业很抽象的东西,你不禁会问,文档中所说的,是不是都有用的呢?实用的理论还是有的,不过有些要点确实太抽象了,抽象到有时候我也不知道用来干吗?

不过,数据协定应该算比较有用的东西,至少在你写WCF服务时还是会经常用到。

 

我们先不管数据协定是什么,就从一个例子入手吧。

1、以管理员身份运行VS,注意,要以管理员身份运行,不然等会儿可能启动不了服务器端。

2、我们这个例子(解决方案)包含两个项目——服务器端和客户端。

3、为了简单起键,我都使用控制台应用程序。

服务器端的实现:

首先定义一个简单的类Employee,现在我们只用普通方式来定义该类,也不添加数据协定特性,在服务器方法协定中把一个Employee实例返回到客户端。我们测一测在不声明数据协定的情况下能不能成功调用。

  1. public class Employee  
  2. {  
  3.     public string Name { getset; }  
  4.     public int Age { getset; }  
  5. }  


以下是服务协定的实现:

  1. [ServiceContract]  
  2. public interface IService  
  3. {  
  4.     [OperationContract]  
  5.     Employee GetAEmployee();  
  6. }  
  7.   
  8. public class MyService : IService  
  9. {  
  10.     public Employee GetAEmployee()  
  11.     {  
  12.         return new Employee { Name = "小朋友", Age = 32 };  
  13.     }  
  14. }  


 

  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         Uri url = new Uri("http://localhost:1233/services");  
  6.         using (ServiceHost host = new ServiceHost(typeof(MyService), url))  
  7.         {  
  8.             // 绑定的Bindong  
  9.             NetHttpBinding binding = new NetHttpBinding();  
  10.             var ep = host.AddDefaultEndpoints();  
  11.             ServiceMetadataBehavior mb = new ServiceMetadataBehavior();  
  12.             mb.HttpGetEnabled = true;  
  13.             mb.HttpGetBinding = binding;  
  14.             host.Description.Behaviors.Add(mb);  
  15.             try  
  16.             {  
  17.                 host.Open();//打开服务  
  18.                 Console.WriteLine("服务已启动。");  
  19.             }  
  20.             catch (Exception ex)  
  21.             {  
  22.                 Console.WriteLine(ex.Message);  
  23.             }  
  24.             Console.ReadKey();  
  25.         }  
  26.     }  
  27. }  


现在先运行一下,确定服务器可以正常启动即可。

 

接下来看看客户端,为了添加服务引用,请到服务器端调试目录(bin\debug)下,以管理身份动行服务器端,然后回到VS,往客户端项目添加服务器端引用。

添加成功后,编写客户端实现代码:

  1. static void Main(string[] args)  
  2. {  
  3.     ws.ServiceClient cl = new ws.ServiceClient();  
  4.     Console.WriteLine("正在调用服务方法……");  
  5.     var em = cl.GetAEmployee();  
  6.     Console.WriteLine("员工信息\n姓名:{0}\n年龄:{1}", em.Name, em.Age);  
  7.   
  8.     Console.ReadKey();  
  9. }  


确保服务器端还在运行,然后调试运行客户端,如果通信成功,你会看到以下结果。

 

这个例子只是说明了,如果在协定方法上出现非基元类型,在客户端引用时,可以生成对应类,至少表明,可被序列化的类型是没有问题的。非基元类型是啥,就是CLR的最简单的类型,如int, bool, float, long..................据说DateTime也是。

我们看看客户端生成的类是啥样的。

与服务器端不同,类实现了两个接口:IExtensibleDataObject,   INotifyPropertyChanged。如果你觉得不好理解,那可以不管它,因为这不影响我们的实际应用。

 

下面我们再把刚才的代码修改一下,在服务器端,我们重新定义一下Employee类,这回我们使用数据协定来标注。

  1. [DataContract]  
  2. public class Employee  
  3. {  
  4.     [DataMember]  
  5.     public string Name { getset; }  
  6.     [DataMember]  
  7.     public int Age { getset; }  
  8.     public string City { getset; }  
  9. }  


接着重新运行一下,注意,客户端的服务引用也要更新。接着再运行一下客户端,从表面上看,似乎和前面没有使用数据协定时没有区别。不过,你还是仔细看看Employee的定义,发现了没有?在Employee类的定义中有一个City属性,但是,在客户端更新服务后,在客户生成的代码中还是没有City属性,换句话说,只是应用了DataMember特性的成员才算是数据成员。

现在,我们再把Employee类修改一下,为City属性也加上DataMember特性。

  1. [DataContract]  
  2. public class Employee  
  3. {  
  4.     [DataMember]  
  5.     public string Name { getset; }  
  6.     [DataMember]  
  7.     public int Age { getset; }  
  8.     [DataMember]  
  9.     public string City { getset; }  
  10. }  


然后更新一下客户端的服务引用,看看这回生成的代理代码中还有没有City属性?

这回在客户端生成的代码中,就见到了City属性了。所以,我们现在明白了一件事,使用数据协定可以灵活控制哪些成员应该被客户端识别。

其实,数据成员也可以用于字段,哪怕是私有字段,看看下面的修改。

  1. [DataContract]  
  2. public class Employee  
  3. {  
  4.     [DataMember]  
  5.     public string Name { getset; }  
  6.     [DataMember]  
  7.     public int Age { getset; }  
  8.     [DataMember]  
  9.     public string City { getset; }  
  10.   
  11.     [DataMember]  
  12.     private string _address;  
  13. }  

现在Employee类多了一个私有字段作为数据成员,看看客户端生成的代码有何变化。


我们也看到了这个私有字段,不过,就是因为它是私人财产,你无法直接拿来用。好,现在我们把Employee类再修改一下。

  1. [DataContract]  
  2. public class Employee  
  3. {  
  4.     [DataMember]  
  5.     public string Name  
  6.     {  
  7.         get { return this._name; }  
  8.         set { this._name = value; }  
  9.     }  
  10.     [DataMember]  
  11.     public int Age  
  12.     {  
  13.         get { return this._age; }  
  14.         set { this._age = value; }  
  15.     }  
  16.     [DataMember]  
  17.     public string City  
  18.     {  
  19.         get { return this._city; }  
  20.         set { this._city = value; }  
  21.     }  
  22.     // 字段列表  
  23.     [DataMember]  
  24.     private string _name;  
  25.     [DataMember]  
  26.     private int _age;  
  27.     [DataMember]  
  28.     private string _city;  
  29. }  


这一次,客户端又会生成什么代码呢?

我们发现,不管在服务器端我们把数据成员定义为公共属性还是私有字段,到了客户端都全变成公共属性了。为了验证这一点,我们把Employee类再次进行修改,把所有的成员都改为私有字段。

  1. [DataContract]  
  2. public class Employee  
  3. {  
  4.     [DataMember]  
  5.     private string Name;  
  6.   
  7.     [DataMember]  
  8.     private int Age;  
  9.   
  10.     [DataMember]  
  11.     private string City;  
  12. }  

由于私有字段不能被外部访问,因此,服务代码也要修改。

  1. public class MyService : IService  
  2. {  
  3.     public Employee GetAEmployee()  
  4.     {  
  5.         return new Employee();  
  6.     }  
  7. }  


 

这样一来,我们看看客户端又会生成啥样子的代码。

很显然,到客户端,所有数据成员都成了公共属性了。这什么会这样呢?其实数据协定是通过XML来传输的,你想一想XML序列化的特点就找到些启发了,XML序列化和反序列化只针对公共成员,所以,如果希望让私有的数据成员也能进行序列化,能做的事情就是把数据成员都变成公共成员。这一点MSDN上也有相关说明。

 

数据协定还有一个很好的用处,那就是隐藏身份,比如我们把Employee作以下修改:

  1. public class MyService : IService  
  2. {  
  3.     public Employee GetAEmployee()  
  4.     {  
  5.         return new Employee() { Name = "小胡", Age = 32, City = "南京" };  
  6.     }  
  7. }  
  8.   
  9. [DataContract(Name = "Worker")]  
  10. public class Employee  
  11. {  
  12.     [DataMember(Name = "Worker_Name")]  
  13.     public string Name { getset; }  
  14.   
  15.     [DataMember(Name = "Worker_Age")]  
  16.     public int Age { getset; }  
  17.   
  18.     [DataMember(Name = "Worker_City")]  
  19.     public string City { getset; }  
  20. }  


这一次,客户端生成的类,类名、属性名全变了,虽然变成了另一个类,但里面的数据还是从服务器端的Employee类传递的。

所以说,数据协定也有隐藏真实身份的作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值