zoukankan      html  css  js  c++  java
  • 《Windows Communication Foundation之旅》系列之四(转载)

    六、定义DataContract

    我在介绍如何定义一个ServiceContract时,举了这样的一个例子,代码如下:
    [ServiceContract]
    public class BookTicket
    {
     [OperationContract]
     public bool Check(Ticket ticket)
     {
      bool flag;
      //logic to check whether the ticket is none;
      return flag;
     }
     [OperationContract]
     private bool Book(Ticket ticket)
     {
      //logic to book the ticket
     }
    }

    ServiceBookTicket中,两个服务方法CheckBook的参数均为Ticket类型。这个类型是自定义类型,根据WCF的要求,该类型必须支持序列化的操作,方才可以在服务方法中作为消息被传递。

    .Net中,除了基本类型如intlongdouble,以及枚举类型和String类型外,一个自定义的类型如要支持序列化操作,应该标记该类型为[Serializable],或者使该类型实现ISerializable接口。而在WCF中,推荐的一种方式是为这些类型标记DataContractAttribute。方法如下:
    [DataContract]
    public class Ticket
    {
    private string m_movieName;

     [DataMember]
     public int SeatNo;
     [DataMember]
     public string MovieName
     {
      get {return m_movieName;}
      set {m_movieName = value;}
     }
     [DataMember]
     private DateTime Time;
    }

    其中,[DataMember]是针对DataContract类的成员所标记的Attribute。与服务类中的OperationContractAttribute相同,DataMemberAttribute与对象的访问限制修饰符(publicinternalprivate等)没有直接关系。即使该成员为private,只要标记了[DataMember],仍然可以被序列化。虽然DataMemberAttribute可以被施加给类型的字段和属性,但如果被施加到static成员时,WCF会忽略该DataMemberAttribute

    当我们为一个类型标注DataContractAttribute时,只有被显式标注了DataMemberAttribute的成员方才支持序列化操作。这一点与SerializableAttribute大相径庭。一个被标记了SerializableAttribute的类型,在默认情况下,其内部的成员,不管是public还是private都支持序列化,除非是那些被施加了NonSerializedAttribute的成员。DataContractAttribute采用这种显式标注法,使得我们更加专注于服务消息的定义,只有需要被传递的服务消息成员,方才被标注DataMemberAttribute

    如果DataContract类中的DataMember成员包含了泛型,那么泛型类型参数必须支持序列化,如下代码所示:
    [DataContract]
    public class MyGeneric
    {
     [DataMember]
     T theData;
    }

    在类MyGeneric中,泛型参数T必须支持序列化。如实例化该对象:
    MyGeneric intObject = new MyGeneric();
    MyGeneric customObject = new MyGeneric();

    对象intObject由于传入的泛型参数为int基本类型,因此可以被序列化;而对象customObject是否能被序列化,则要看传入的泛型参数CustomType类型是否支持序列化。

    DataContractNamespaceName来唯一标识,我们可以在DataContractAttributeNamespace属性、Name属性中进行设置。如未设置DataContractName属性,则默认的名字为定义的类型名。DataMember也可以通过设置Name属性,默认的名字为定义的成员名。如下代码所示:
    namespace MyCompany.OrderProc
    {
        [DataContract]
        public class PurchaseOrder
        {
            // DataMember
    名字为默认的Amount;
            [DataMember]
            public double Amount;
           
            // Name
    属性将重写默认的名字Ship_to,此时DataMember名为Address;
            [DataMember(Name = “Address”)]
            public string Ship_to;
        }
        //Namespace
    为默认值:
        // http://schemas.datacontract.org/2004/07/MyCompany.OrderProc
        //
    此时其名为PurchaseOrder而非MyInvoice
        [DataContract(Name = “PurchaseOrder”)]
        public class MyInvoice
        {
            // Code not shown.
        }

        // 其名为Payment而非MyPayment
        // Namespace
    被设置为http://schemas.example.com
        [DataContract(Name = “Payment”,
            Namespace = “http://schemas.example.com“)]
        public class MyPayment
        {
            // Code not shown.
        }
    }

    // 重写MyCorp.CRM下的所有DataContractNamespace
     [assembly:ContractNamespace(
        ClrNamespace = “MyCorp.CRM”,
        Namespace= “http://schemas.example.com/crm“)]
    namespace MyCorp.CRM
    {
        //
    此时Namespace被设置为http://schemas.example.com/crm.
        //
    名字仍然为默认值Customer
        [DataContract]
        public class Customer
        {
            // Code not shown.
        }
    }

    由于DataContract将被序列化以及反序列化,因此类型中成员的顺序也相当重要,在DataMemberAttribute中,提供了Order属性,用以设置成员的顺序。在WCF中对成员的序列化顺序规定如下:
    1
    、默认的顺序依照字母顺序;
    2
    、如成员均通过Order属性指定了顺序,且顺序值相同,则以字母顺序;
    3
    、未指定Order属性的成员顺序在指定了Order顺序之前;
    4
    、如果DataContract处于继承体系中,则不管子类中指定的Order值如何,父类的成员顺序优先。

    下面的代码很好的说明了DataMember的顺序:
    [DataContract]
    public class BaseType
    {
        [DataMember] public string zebra;
    }

    [DataContract]
    public class DerivedType : BaseType
    {
        [DataMember(Order = 0)] public string bird;
        [DataMember(Order = 1)] public string parrot;
        [DataMember] public string dog;
        [DataMember(Order = 3)] public string antelope;
        [DataMember] public string cat;
        [DataMember(Order = 1)] public string albatross;
    }

    序列化后的XML内容如下:
    <DerivedType>
        <zebra/> 
        <cat/>
        <dog/> 
        <bird/>
        <albatross/>
        <parrot/>
        <antelope/>
    </DerivedType>

    因为成员zebra为父类成员,首先其顺序在最前面。catdog未指定Order,故在指定了Order的其它成员之前,两者又按照字母顺序排列。parrotalbatross均指定Order值为1,因此也按照字母顺序排列在Order值为0bird之后。

    判断两个DataContract是否相同,应该根据DataContractNamespaceName,以及DataMemberNameOrder来综合判断。例如下面代码所示的类CustomerPerson其实是同一个DataContract
    [DataContract]
    public class Customer
    {
        [DataMember]
        public string fullName;

        [DataMember]
        public string telephoneNumber;
    }

    [DataContract(Name=”Customer”)]
    public class Person
    {
        [DataMember(Name = “fullName”)]
        private string nameOfPerson;

        private string address;

        [DataMember(Name= “telephoneNumber”)]
        private string phoneNumber;
    }

    再例如下面代码所示的类Coords1Coords2Coords3也是相同的DataContract,而类Coords4则因为顺序不同,因而与前面三个类是不同的:
    [DataContract(Name= “Coordinates”)]
    public class Coords1
    {
        [DataMember] public int X;
        [DataMember] public int Y;
    }

    [DataContract(Name= “Coordinates”)]
    public class Coords2
    {
         [DataMember] public int Y;
         [DataMember] public int X;
    }

    [DataContract(Name= “Coordinates”)]
    public class Coords3
    {
         [DataMember(Order=2)] public int Y;
         [DataMember(Order=1)] public int X;
    }
    [DataContract(Name= “Coordinates”)]
    public class Coords4
    {
         [DataMember(Order=1)] public int Y;
         [DataMember(Order=2)] public int X;
    }

    DataContract处于继承体系时,还需要注意的是对象的多态问题。如果在服务端与客户端之间要传递消息,经常会涉及到类型的动态绑定。根据规定,如果消息的类型是子类类型,那么发送消息一方就不能传递基类类型。相反,如果消息类型是父类类型,那么发送消息一方就可以是父类本身或者其子类。从这一点来看,WCF的规定是与面向对象思想并行不悖的。但是可能存在的问题是,当消息类型定义为父类类型,而发送消息一方传递其子类时,服务端有可能对该子类类型处于未知状态,从而不能正常地反序列化。所以,WCFDataContract提供了KnownTypeAttribute,通过设置它来告知服务端可能存在的动态绑定类类型。

    举例来说,如果我们定义了这样三个类:
    [DataContract]
    public class Shape { }

    [DataContract(Name = “Circle”)]
    public class CircleType : Shape { }

    [DataContract(Name = “Triangle”)]
    public class TriangleType : Shape { }

    然后在类CompanyLogo中定义Shape类型的字段,如下所示:
    [DataContract]
    public class CompanyLogo
    {
        [DataMember]
        private Shape ShapeOfLogo;
        [DataMember]
        private int ColorOfLogo;
    }

    此时的CompanyLogo类由于正确的设置了[DataContract][DataMember],而Shape类型也是支持序列化的,因此该类型是可以被序列化的。然而一旦客户端在调用CompanyLogo类型的对象时,为字段ShapeOfLogo设置其值为CircleTypeTriangleType类型的对象,就会发生反序列化错误,因为服务端并不知道CircleTypeTriangleType类型,从而无法进行正确的匹配。所以上述的CompanyLogo类的定义应修改如下:
    [DataContract]
    [KnownType(typeof(CircleType))]
    [KnownType(typeof(TriangleType))]
    public class CompanyLogo
    {
        [DataMember]
        private Shape ShapeOfLogo;
        [DataMember]
        private int ColorOfLogo;
    }

    类的继承如此,接口的实现也是同样的道理,如下例所示:
    public interface ICustomerInfo
    {
        string ReturnCustomerName();
    }

    [DataContract(Name = “Customer”)]
    public class CustomerType : ICustomerInfo
    {
        public string ReturnCustomerName()
        {
            return “no name”;
        }
    }

    [DataContract]
    [KnownType(typeof(CustomerType))]
    public class PurchaseOrder
    {
        [DataMember]
        ICustomerInfo buyer;

        [DataMember]
        int amount;
    }

    由于PurchaseOrder中定义了ICustomerInfo接口类型的字段,如要该类能被正确的反序列化,就必须为类PurchaseOrder加上[KnownType(typeof(CustomerType))]的标注。

    对于集合类型也有相似的规定。例如Hashtable类型,其内存储的均为object对象,但实际设置的值可能是一些自定义类型,此时也许要通过KnownType进行标注。例如在类LibraryCatalog中,定义了Hashtable类型的字段theCatalog。该字段可能会设置为Book类型和Magazine类型,假定Book类型和Magazine类型均被定义为DataContract,则类LibraryCatalog的正确定义应如下所示:
    [DataContract]
    [KnownType(typeof(Book))]
    [KnownType(typeof(Magazine))]
    public class LibraryCatalog
    {
        [DataMember]
        System.Collections.Hashtable theCatalog;
    }

    如果在一个DataContract中,定义一个object类型的字段。由于object类型是所有类型的父类,所以需要我们利用KnownType标明客户端允许设置的类型。例如类MathOperationData
    [DataContract]
    [KnownType(typeof(int[]))]
    public class MathOperationData
    {
        private object numberValue;
        [DataMember]
        public object Numbers
        {
            get { return numberValue; }
            set { numberValue = value; }
        }
        //[DataMember]
        //public Operation Operation;
    }

    属性Numbers其类型为object,而KnownType设置的类型是int[],因此可以接受的类型就包括:整型,整型数组以及List类型。如下的调用都是正确的:
    static void Run()
    {
        MathOperationData md = new MathOperationData();

        int a = 100;
        md.Numbers = a;

        int[] b = new int[100];
        md.Numbers = b;
       
        List c = new List();
        md.Numbers = c;   
    }

    但如果设置Number属性为ArrayList,即使该ArrayList对象中元素均为int对象,也是错误的:
    static void Run()
    {
        MathOperationData md = new MathOperationData();

    ArrayList d = new ArrayList();
        md.Numbers = d;
    }

    一旦一个DataContract类型标注了KnownTypeAttribute,则该Attribute的作用域可以施加到其子类中,如下所示:
    [DataContract]
    [KnownType(typeof(CircleType))]
    [KnownType(typeof(TriangleType))]
    public class MyDrawing
    {
        [DataMember]
        private object Shape;
        [DataMember]
        private int Color;
    }

    [DataContract]
    public class DoubleDrawing : MyDrawing
    {
        [DataMember]
        private object additionalShape;
    }

    虽然DoubleDrawing没有标注KnowTypeAttribute,但其字段additionalShape仍然可以被设置为CircleType类型或TriangleType类型,因为其父类已经被设置为KnowTypeAttribute

    注:KnowTypeAttribute可以标注类和结构,但不能标注接口。此外,DataContract同样不能标注接口,仅可以标注类、结构和枚举。要使用DataContractAttributeDataMemberAttributeKnownTypeAttribute,需要添加WinFx版本的System.Runtime.Serialization程序集的引用。
  • 相关阅读:
    IP子网掩码格式转换
    错误: symbol lookup error: /usr/local/lib/libreadline.so.6: undefined symbol: PC
    postgresql删除属性
    postgresql 修改属性
    嵌套json的查询
    嵌套json
    关于array_agg 函数
    修改jsonb的属性
    Python中exec的使用
    RHSA-2017:2930-重要: 内核 安全和BUG修复更新(需要重启、存在EXP、本地提权、代码执行)
  • 原文地址:https://www.cnblogs.com/chorrysky/p/630424.html
Copyright © 2011-2022 走看看