WCF初探-17:WCF数据协定之等效性
数据协定等效性特点概述
- 对于客户端要将某种类型的数据成功发送到服务,或者服务要将数据成功发送到客户端的情况,接收端上并不一定必须存在此发送数据类型。 唯一的要求是两种类型的数据协定应该等效。
- 要使数据协定等效,其命名空间和名称必须相同。 此外,某一端上的每个数据成员还必须在另一端上具有等效的数据成员。注意,数据协定名称和命名空间以及数据成员名称均区分大小写。
- 要使数据成员等效,其名称必须相同。 此外,它们还必须表示同一类型的数据,也就是说,其数据协定必须等效。
- 如果同一端(发送方或接收方)存在两种类型,而其数据协定又不等效(例如,它们的数据成员不同),则不应为它们指定相同的名称和命名空间。 否则,可能会引发异常。
- 使用 DataMemberAttribute 类的 Order 属性可能会影响数据协定等效性。 其成员必须以相同顺序出现,数据协定才能等效。
数据协定等效性说明
- 在上图中,我们在服务端定义了数据协定为User和Person的类,他们在客户端都会生成和标记3一样的客户端数据契约类型。因此,我们称User和Person是具有等效性的数据契约。
- 数据协定的等效性要求只与上面的数据协定等效性特点概述有关,与数据协定本身的类定义无关。也就是说,只要在服务端定义的数据契约类型在客户端代理类中生成相同的数据契约,我们就称服务端的这些数据协定具有等效性。
- 有了上述特点,我们就可以在服务端任意替换具有等效性的数据协定,而不必修改和重新生成客户端,客户端都能正常运行
数据协定等效性示例
- 解决方案如下:
- 工程结构说明
- Service:类库类型,WCF服务端程序。IUserInfo.cs代码如下:
using System.ServiceModel; using System.Runtime.Serialization; namespace Service { [ServiceContract] public interface IUserInfo { [OperationContract] User[] GetInfo(); } [DataContract] public class User { [DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } [DataMember] public string Nationality { get; set; } } }
UserInfo.cs代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Service { public class UserInfo:IUserInfo { public User[] GetInfo() { List<User> Users = new List<User>(); Users.Add(new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" }); Users.Add(new User { ID = 2, Name = "TOM", Age = 18, Nationality = "JAPAN" }); Users.Add(new User { ID = 3, Name = "SMITH", Age = 22, Nationality = "KOREA" }); Users.Add(new User { ID = 4, Name = "ALENCE", Age = 21, Nationality = "INDIA" }); Users.Add(new User { ID = 5, Name = "JOHN", Age = 22, Nationality = "SINGAPORE" }); return Users.ToArray(); } } }
2. Host:控制台应用程序,WCF服务承载程序。添加对Service程序集的引用,完成以下代码和配置后就可以寄宿服务。Program.cs的代码如下:
using System; using System.ServiceModel; using Service; namespace Host { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(UserInfo))) { host.Opened += delegate { Console.WriteLine("服务已经启动,按任意键终止!"); }; host.Open(); Console.Read(); } } } }
App.config的代码如下:
<?xml version="1.0"?> <configuration> <system.serviceModel> <services> <service name="Service.UserInfo" behaviorConfiguration="mexBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:1234/UserInfo/"/> </baseAddresses> </host> <endpoint address="" binding="wsHttpBinding" contract="Service.IUserInfo" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="mexBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
我们通过svcutil.exe工具生成客户端代理类和客户端的配置文件
svcutil.exe是一个命令行工具,位于路径C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin下,我们可以通过命令行运行该工具生成客户端代理类
- 在运行中输入cmd打开命令行,输入 cd C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin
- 输入svcutil.exe /out:f:\UserInfoClient.cs /config:f:\App.config http://localhost:1234/UserInfo
3. Client:控制台应用程序,客户端调用程序。将生成的UserInfoClient.cs和App.config复制到Client的工程目录下,完成客户端调用代码。Program.cs的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Service; namespace Client { class Program { static void Main(string[] args) { UserInfoClient proxy = new UserInfoClient(); User[] Users = proxy.GetInfo(); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality"); for (int i = 0; i < Users.Length; i++) { Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", Users[i].ID.ToString(), Users[i].Name.ToString(), Users[i].Age.ToString(), Users[i].Nationality.ToString()); } Console.Read(); } } }
4. 程序运行效果如下:
数据协定等效性验证
- 接下来,我们修改服务端的数据协定,并且修改操作协定的实现代码。IUserInfo.cs的代码如下:
using System.ServiceModel; using System.Runtime.Serialization; namespace Service { [ServiceContract] public interface IUserInfo { [OperationContract] Person[] GetInfo(); } [DataContract(Name = "User")] public class Person { [DataMember] public int ID { get; set; } [DataMember(Name = "Name")] public string 姓名 { get; set; } [DataMember] public int Age { get; set; } [DataMember] public string Nationality { get; set; } } }
UserInfo.cs的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Service { public class UserInfo:IUserInfo { public Person[] GetInfo() { List<Person> Persons = new List<Person>(); Persons.Add(new Person { ID = 1, 姓名 = "JACK", Age = 20, Nationality = "CHINA" }); Persons.Add(new Person { ID = 2, 姓名 = "TOM", Age = 18, Nationality = "JAPAN" }); Persons.Add(new Person { ID = 3, 姓名 = "SMITH", Age = 22, Nationality = "KOREA" }); Persons.Add(new Person { ID = 4, 姓名 = "ALENCE", Age = 21, Nationality = "INDIA" }); Persons.Add(new Person { ID = 5, 姓名 = "JOHN", Age = 22, Nationality = "SINGAPORE" }); return Persons.ToArray(); } } }
- 修改后我们保存并重新生成Host程序,Client程序不做任何操作。运行结果和先前一样,说明两次定义的数据协定时等效的。程序运行效果如下:
- 接下来,我们再次修改数据协定,为数据协定的成员添加Order属性,修改代码如下所示,修改后我们重新编译Host,运行程序。运行后我们发现程序没有报错。但是ID和Age的值都变成了0,说明契约的等效性受Order的影响。
[DataContract(Name = "User")] public class Person { [DataMember(Order = 2)] public int ID { get; set; } [DataMember(Name = "Name")] public string 姓名 { get; set; } [DataMember(Order = 3)] public int Age { get; set; } [DataMember(Order = 0)] public string Nationality { get; set; } }