• 传说中的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();
                }
            }
    复制代码

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

    客户端测试代码如下:

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


    其运行结果如下:

    下面我们继续实验。

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

    复制代码
        [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}
    联系电话:{1}
    " +
                            "地址信息:-----------
    " +
                            "省份:{2}
    " +
                            "市区:{3}
    " +
                            "详细地址:{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}
    ", 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
    {
    。。。。。
    }


    这时候,再运行一下。

    很可惜,还是报错了,

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

    复制代码
        [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的构造函数中传递该方法的名字。看看这回调用能否成功?


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

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

  • 相关阅读:
    APS.NET MVC + EF (01)---Linq和Lambda表达式
    APS.NET MVC + EF (00)---C#基础
    Asp.net MVC 之ActionResult
    在ASP.NET MVC中加载部分视图的方法及差别
    Entity Framework Codefirst数据迁移
    Entity Framework Codefirst的配置步骤
    02:PostgreSQL Character Sets
    安装postgis,使用postgis导入shapefile的步骤总结
    WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析
    DEM数据及其他数据下载
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/3663864.html
Copyright © 2020-2023  润新知