读书人

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

发布时间: 2012-11-23 22:54:33 作者: rapoo

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

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

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

    [DataContract]    public class Student    {        [DataMember]        public string Name;        [DataMember]        public string Phone;        [DataMember]        public AddrInfo Address;    }    [DataContract]    public class AddrInfo    {        [DataMember]        public string Province;        [DataMember]        public string City;        [DataMember]        public string DetailAddr;    }


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

    [ServiceContract(Namespace = "MyNamespace")]    public interface IService    {        [OperationContract]        Student GetStudentInfo();    }    public class MyService : IService    {        public Student GetStudentInfo()        {            Student stu = new Student();            AddrInfo info = new AddrInfo();            info.Province = "广东省";            info.City = "佛山市";            info.DetailAddr = "火星路-300号";            stu.Name = "小陈";            stu.Phone = "1388888888";            stu.Address = info;            return stu;        }    }

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

        static void Main(string[] args)        {            // 服务器基址            Uri baseAddress = new Uri("http://localhost:1378/services");            // 声明服务器主机            using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddress))            {                // 添加绑定和终结点                WSHttpBinding binding = new WSHttpBinding();                host.AddServiceEndpoint(typeof(IService), binding, "/test");                // 添加服务描述                host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });                try                {                    // 打开服务                    host.Open();                    Console.WriteLine("服务已启动。");                }                catch (Exception ex)                {                    Console.WriteLine(ex.Message);                }                Console.ReadKey();            }        }


在客户端生成的代码中,两个类都可以正确生成。
传说中的WCF(六):数据协定(b)

客户端测试代码如下:

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


其运行结果如下:

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

下面我们继续实验。

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

    [DataContract]    public class Student    {        [DataMember]        public string Name;        [DataMember]        public string Phone;        [DataMember]        public AddrInfo Address;        [DataMember]        public object Scores;    }

    public class MyService : IService    {        public Student GetStudentInfo()        {            Student stu = new Student();            AddrInfo info = new AddrInfo();            info.Province = "广东省";            info.City = "佛山市";            info.DetailAddr = "火星路-300号";            stu.Name = "小陈";            stu.Phone = "1388888888";            stu.Address = info;            Dictionary<string, float> m_scores = new Dictionary<string, float>();            m_scores.Add("语文", 97f);            m_scores.Add("英语", 64.5f);            m_scores.Add("数学", 38f);            m_scores.Add("历史", 77.6f);            m_scores.Add("地理", 82.3f);            stu.Scores = m_scores;            return stu;        }    }


客户端测试代码改为:

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


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

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

    [DataContract]    [KnownType(typeof(Dictionary<string, float>))]    public class Student    {        。。。。。    }


这时候,再运行一下。

很可惜,还是报错了,

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

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

    [DataContract]    [KnownType("GetKnowTypes")]    public class Student    {        [DataMember]        public string Name;        [DataMember]        public string Phone;        [DataMember]        public AddrInfo Address;        [DataMember]        public object Scores;        static Type[] GetKnowTypes()        {            return new Type[] { typeof(Dictionary<string,float>) };        }    }

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

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


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

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

读书人网 >编程

热点推荐