• 重温WCF之数据契约和序列化(四)


    一、数据契约

    1.使用数据协定可以灵活控制哪些成员应该被客户端识别。

        [DataContract]
        public class Employee
        {
            [DataMember]
            public string Name { get; set; }
            [DataMember]
            public int Age { get; set; }
            [DataMember]
            public string City { get; set; }
        }
        
        [ServiceContract]
        public interface IService
        {
            [OperationContract]
            Employee GetAEmployee();
        }
    
        public class MyService : IService
        {
            public Employee GetAEmployee()
            {
                return new Employee { Name = "小朋友", Age = 32 };
            }
        }
        class Program
        {
           static void Main(string[] args)
            {
                using (ServiceHost host = new ServiceHost(typeof(Service1)))
                {
                    host.AddServiceEndpoint(typeof(IService1), new WSHttpBinding(), "http://127.0.0.1:8888/service1");
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/service");  //httpGetUrl客户端引用的地址
                    host.Description.Behaviors.Add(behavior);
                    host.Opened += delegate
                    {
                        Console.WriteLine("服务已启动");
                        Console.ReadKey();
                    };
                    host.Open();
                }
            }
        }

    如果把Employee类中的某个成员的DataMember属性去掉,则在客户端代理类中就不会生成该属性

    2.数据协定也有隐藏真实身份的作用

        [DataContract(Name = "Worker")]
        public class Employee
        {
            [DataMember(Name = "Worker_Name")]
            public string Name { get; set; }
    
            [DataMember(Name = "Worker_Age")]
            public int Age { get; set; }
    
            [DataMember(Name = "Worker_City")]
            public string City { get; set; }
        }

    这样在客户端生成的代理类的成员名称就和服务器端不一样

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

    [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>) };
            }
        }
        [DataContract]
        public class AddrInfo
        {
            [DataMember]
            public string Province;
            [DataMember]
            public string City;
            [DataMember]
            public string DetailAddr;
        }
          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)
            {
                using (ServiceHost host = new ServiceHost(typeof(Service1)))
                {
                    host.AddServiceEndpoint(typeof(IService1), new WSHttpBinding(), "http://127.0.0.1:8888/service1");
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:8888/service");  //httpGetUrl客户端引用的地址
                    host.Description.Behaviors.Add(behavior);
                    host.Opened += delegate
                    {
                        Console.WriteLine("服务已启动");
                        Console.ReadKey();
                    };
                    host.Open();
                }
            }
    
    
            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();
            }

    使用[ServiceKnownType(typeof(Order))]放在服务契约上,使用[KnownType(typeof(Order))]放在数据契约上

    [ServiceContract]
        [ServiceKnownType(typeof(Order))]//可以放到这里
        public interface IService1
        {
            [OperationContract]
            //[ServiceKnownType(typeof(Order))]也可以放到这里
            void AddOrder(OrderBase order);
        }
        [DataContract]
       // [KnownType(typeof(Order))]也可以放到这里
        public abstract class OrderBase
        {
            [DataMember]
            public Guid ID { get; set; }
    
            [DataMember]
            public DateTime Date { get; set; }
    
            [DataMember]
            public string Customer { get; set; }
    
            [DataMember]
            public string ShipAddress { get; set; }
        }
        [DataContract]
        public class Order : OrderBase
        {
            [DataMember]
            public double TotalPrice { get; set; }
        }
    
        public class Service1 : IService1
        {
            public void AddOrder(OrderBase order)
            {
                Console.WriteLine(order.Customer);
            }
        }
    
        public class Program
        {
            static void Main(string[] args)
            {
                using (Service1Client client = new Service1Client())
                {
                    Order order = new Order() { Customer = "yxl" };
                    client.AddOrder(order);
    
                }
                Console.ReadKey();
            }
        }

    如果不标识ServiceKnownType或KnownType,客户端将不会生成Order类

    二、序列化

    1.NET序列化机制

       在.NET Framework 3.0之前,提供了3中序列化器,序列化器理解为把可序列化的类型序列化成XML的类。这三种序列化器分别是BinaryFormatter、SoapFormatter和XmlSerializer类。下面分别介绍下这3种序列化器。

    • BinaryFormatter类:把.NET Object序列化成二进制格式。在这个过程,对象的公共字段和私有字段以及类名称(包括类的程序集名),将转换成成字节流。
    • SoapFormatter类:把.NET Object序列化成SOAP格式,SOAP是一种轻量、简单的,基于XML的协议。只序列化字段,包括公共字段和私有字段
    • XmlSerializer类:该类仅仅序列化公共字段和属性,且不保存类型的保真度。

      对于这三种序列化机制,BinaryFormatter二进制序列化的优点是:性能高,但是不能跨平台。而SoapFormatter,XmlSerializer的优点是:跨平台、互操作性好,并且可读性强,但是传输性能不及BinaryFormatter。

    原有的格式化器需要将类型的程序集及版本控制信息持久化到流中,以保证对象被反序列化为正确的类型,这种方式一定程度上妨碍面向服务交互。原因如下:BinaryFormatter和SoapFormatter具备类型保真,它们要求其所在程序集、版本等信息必须完全一致,如果不同则视为是不同类型的对象,这就要求客户端必须拥有原有的.NET 程序集。面向服务的交互方式应该更倾向于平台无关,并最大限度的解耦,因此这2个格式化器显然与WCF有些不搭。再看XmlSerializer格式化器,在功能上与DataContractSerializer格式器类似,但是为何没有使用XmlSerializer格式器而是引人新格式器,我的理解如下:在SOA服务的构建过程中,提倡契约优先的构建原则,也就是说我们应该优先构建契约后构建具体的代码,在构建过程中并不考虑其具体的序列化过程。(契约优先相对于代码优先在设计层面具有更高的抽象程度,降低了服务设计与平台的耦合度,因此我更倾向于契约优先的开发顺序。)此外,XmlSerializer为如何将数据表示成XML提供了精确的控制,DataContractSerializer则提供了较少的控制,所以DataContractSerializer的序列化过程是可预知的,容易优化,所以它与XmlSerializer相比有更好的性能(我查到的数字是提升了约10%)。  

    2.WCF中序列化机制

    在WCF中,提供了专门用来序列化和反序列操作的类,该类就是DataContractSerializer类。一般而言,WCF会自动选择使用DataContractSerializer来对可序列话数据契约进行序列化,不需要开发者直接调用。WCF除了支持DataContractSerializer类来进行序列化外,还支持另外两种序列化器,这两种序列化器分别为:XMLSerializer(定义在System.XML.Serialization namespace)和NetDataContractSerializer (定义在System.XML.Serialization namespace)。XmlSerializer类不是WCF专用的类,Asp.net Web服务统一使用该类作为序列化器,但XmlSerializer类支持的类少于DataContractSerializer列支持的类型,但它允许对生成的XML进行更多的控制,并且支持更多的XML架构定义语言(XSD)标准。它不需要在可序列化类上有任何声明性的属性。

      默认情况下,WCF 使用 DataContractSerializer 类来序列化数据类型。 此序列化程序支持下列类型:

    • 基元类型(如:整数、字符串和字节数组)以及某些特殊类型(如 XmlElement 和 DateTime)。
    • 数据协定类型(用 DataContractAttribute 属性标记的类型)。
    • 用 SerializableAttribute 属性标记的类型,包括实现 ISerializable 接口的类型。
    • 实现 IXmlSerializable 接口的类型。
    • 许多常见集合类型,包括许多泛型集合类型。

      DataContractSerializer类与NetDataContractSerializer类类似,它们之间主要的区别在于:在使用NetDataContractSerializer进行序列化时,不需要指定序列化的类型,如:

    NetDataContractSerializer serializer =
        new NetDataContractSerializer();  // 不需要明确指定序列化的类型
    serializer.WriteObject(writer, p);
    
    // 而使用DataContractSerializer需要明确指定序列化的类型
    DataContractSerializer serializer =
                new DataContractSerializer(typeof(Order)); // 需要明确指定序列化的类型
            serializer.WriteObject(writer, p);

    SerializableAttribute与DataContract异同。

    相同点:都是标记类型为可序列化类型

    不同点:在于序列化的成员不一样,DataContract是Opt-in(明确参与)的方式,即使用DataMember特性明确标识哪些成员需要序列化,而Serializable是Opt-out方式,即使用NoSerializable特性明确标识不参与序列化的成员。

    我个人意见,DataContractSerializable就用在它应该用的地方吧,如果不是用WCF,还是不要用它了,它的序列化结果有一些微软专属的东西。对于来自网络的松散Xml接口数据,XmlSerializer是不二之选。如果想把对象完整地保存下来(数据与状态),同时又不需要被人看。那就用BinaryFormatter吧,SerializableAttribute默认是使用BinaryFormatter序列化的。

  • 相关阅读:
    分页封装实用工具类及其使用方法
    Oracle
    [置顶] Android高德地图显示气泡框
    设计模式 笔记 观察者模式
    数据质量,中国希望
    谁更胜一筹:技术解析 Google App Engine 和 Amazon EC2
    GZIP Http Servlet Response
    谁更胜一筹:技术解析 Google App Engine 和 Amazon EC2
    腾讯对外发布微博开放平台 API
    GZIP Http Servlet Response
  • 原文地址:https://www.cnblogs.com/yxlblogs/p/3765681.html
Copyright © 2020-2023  润新知