传说中的WCF(6):数据协定(b)

我们继续来吹牛,上一回我们吹了数据协定的一部分内容,今天我们接着来做实验。好的,实验之前先说一句:实验有风险,写代码须谨慎。

实验开始!现在,我们定义两个带数据协定的类——Student和AddrInfo。

  1. [DataContract]  
  2. public class Student  
  3. {  
  4.     [DataMember]  
  5.     public string Name;  
  6.     [DataMember]  
  7.     public string Phone;  
  8.     [DataMember]  
  9.     public AddrInfo Address;  
  10. }  
  11.   
  12. [DataContract]  
  13. public class AddrInfo  
  14. {  
  15.     [DataMember]  
  16.     public string Province;  
  17.     [DataMember]  
  18.     public string City;  
  19.     [DataMember]  
  20.     public string DetailAddr;  
  21. }  


这两个类有一个特征,Student类的Address字段是一个AddrInfo对象,而我们的服务定义如下:

  1. [ServiceContract(Namespace = "MyNamespace")]  
  2. public interface IService  
  3. {  
  4.     [OperationContract]  
  5.     Student GetStudentInfo();  
  6. }  
  7.   
  8. public class MyService : IService  
  9. {  
  10.     public Student GetStudentInfo()  
  11.     {  
  12.         Student stu = new Student();  
  13.         AddrInfo info = new AddrInfo();  
  14.         info.Province = "广东省";  
  15.         info.City = "佛山市";  
  16.         info.DetailAddr = "火星路-300号";  
  17.         stu.Name = "小陈";  
  18.         stu.Phone = "1388888888";  
  19.         stu.Address = info;  
  20.         return stu;  
  21.     }  
  22. }  

方法返回的Student对象,那么,我们来测试一下,AddrInfo能不能被成功序列化和反序列化。下面是注册和启动服务的代码:

  1. static void Main(string[] args)  
  2. {  
  3.     // 服务器基址  
  4.     Uri baseAddress = new Uri("http://localhost:1378/services");  
  5.     // 声明服务器主机  
  6.     using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddress))  
  7.     {  
  8.         // 添加绑定和终结点  
  9.         WSHttpBinding binding = new WSHttpBinding();  
  10.         host.AddServiceEndpoint(typeof(IService), binding, "/test");  
  11.         // 添加服务描述  
  12.         host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });  
  13.         try  
  14.         {  
  15.             // 打开服务  
  16.             host.Open();  
  17.             Console.WriteLine("服务已启动。");  
  18.         }  
  19.         catch (Exception ex)  
  20.         {  
  21.             Console.WriteLine(ex.Message);  
  22.         }  
  23.         Console.ReadKey();  
  24.     }  
  25. }  


在客户端生成的代码中,两个类都可以正确生成。

客户端测试代码如下:

  1. static void Main(string[] args)  
  2. {  
  3.     WS.ServiceClient cli = new WS.ServiceClient();  
  4.     WS.Student stu = cli.GetStudentInfo();  
  5.     string msg = "学生姓名:{0}\n联系电话:{1}\n" +  
  6.                 "地址信息:-----------\n" +  
  7.                 "省份:{2}\n" +  
  8.                 "市区:{3}\n" +  
  9.                 "详细地址:{4}";  
  10.     Console.WriteLine(msg, stu.Name, stu.Phone, stu.Address.Province, stu.Address.City, stu.Address.DetailAddr);  
  11.     Console.ReadKey();  
  12. }  


其运行结果如下:

 

 

下面我们继续实验。

每个学生可能有多个学科的成绩,因此,我们为学生类再添加一个成绩属性。

  1. [DataContract]  
  2. public class Student  
  3. {  
  4.     [DataMember]  
  5.     public string Name;  
  6.     [DataMember]  
  7.     public string Phone;  
  8.     [DataMember]  
  9.     public AddrInfo Address;  
  10.     [DataMember]  
  11.     public object Scores;  
  12. }  

  1. public class MyService : IService  
  2. {  
  3.     public Student GetStudentInfo()  
  4.     {  
  5.         Student stu = new Student();  
  6.         AddrInfo info = new AddrInfo();  
  7.         info.Province = "广东省";  
  8.         info.City = "佛山市";  
  9.         info.DetailAddr = "火星路-300号";  
  10.         stu.Name = "小陈";  
  11.         stu.Phone = "1388888888";  
  12.         stu.Address = info;  
  13.         Dictionary<stringfloat> m_scores = new Dictionary<stringfloat>();  
  14.         m_scores.Add("语文", 97f);  
  15.         m_scores.Add("英语", 64.5f);  
  16.         m_scores.Add("数学", 38f);  
  17.         m_scores.Add("历史", 77.6f);  
  18.         m_scores.Add("地理", 82.3f);  
  19.         stu.Scores = m_scores;  
  20.         return stu;  
  21.     }  
  22. }  


客户端测试代码改为:

  1. static void Main(string[] args)  
  2. {  
  3.     WS.ServiceClient cli = new WS.ServiceClient();  
  4.     WS.Student stu = cli.GetStudentInfo();  
  5.     string msg = "学生姓名:{0}\n联系电话:{1}\n" +  
  6.                 "地址信息:-----------\n" +  
  7.                 "省份:{2}\n" +  
  8.                 "市区:{3}\n" +  
  9.                 "详细地址:{4}";  
  10.     Console.WriteLine(msg, stu.Name, stu.Phone, stu.Address.Province, stu.Address.City, stu.Address.DetailAddr);  
  11.     Console.WriteLine("---------------------------------------");  
  12.     Console.WriteLine("学生成绩单:");  
  13.     Dictionary<stringfloat> scores = stu.Scores as Dictionary<stringfloat>;  
  14.     foreach (var item in scores)  
  15.     {  
  16.         Console.Write("{0}:{1}\n", item.Key, item.Value);  
  17.     }  
  18.   
  19.     Console.ReadKey();  
  20. }  


现在来测试,就会发生异常,因为我们为Student类加的成绩属性是object类型,而我们在协定方法中给它赋的是Dictionary<string, float>类型,这样一来,就算可以序列化也不能被反序列化,因为Dictionary<string, float>无法识别。

现在,我们再为Student类添加一个KnownType特性,看它能不能识别。

  1. [DataContract]  
  2. [KnownType(typeof(Dictionary<stringfloat>))]  
  3. public class Student  
  4. {  
  5.     。。。。。  
  6. }  


这时候,再运行一下。

很可惜,还是报错了,

 

怎么办呢?不要放弃,我们继续把学生类修改一下。

  1. [DataContract]  
  2. [KnownType("GetKnowTypes")]  
  3. public class Student  
  4. {  
  5.     [DataMember]  
  6.     public string Name;  
  7.     [DataMember]  
  8.     public string Phone;  
  9.     [DataMember]  
  10.     public AddrInfo Address;  
  11.     [DataMember]  
  12.     public object Scores;  
  13.   
  14.     static Type[] GetKnowTypes()  
  15.     {  
  16.         return new Type[] { typeof(Dictionary<string,float>) };  
  17.     }  
  18. }  

GetKnowTypes方法是静态方法,KnownType的构造函数中传递该方法的名字。看看这回调用能否成功?


坚持就是胜利,看到了吧,这回OK了!高兴吧。

 

这里我们可以总结一下,在一些比较复杂的类型无法反序列化(不能识别类型)的时候,就得考虑使用KnownTypeAttribute来标注可能涉及到的外部类型,但如果遇到像泛型这些较为复杂的类型,就要考虑在带数据协定的类中添加一个静态方法,该方法返回Type 的IEnumerable,一般是Type[]就可以了,而在KnownTypeAttribute的构造函数中使用这个方法的名字

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值