1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Data Contracts in WCF

Discussion in 'ASP.NET' started by MinalS, Nov 13, 2014.

  1. MinalS

    MinalS New Member

    Joined:
    Jul 8, 2014
    Messages:
    138
    Likes Received:
    30
    Trophy Points:
    0
    The [ DataContract ] attribute is used to define the data contract in a WCF service. It specifies which class should be presented as XSD. It contains a [ DataMember ] attribute that defines the class members to be included in the external representation. The DataContractSerializer is used to serialize the objects to XML through the data contract rules.

    The following figure represents the .NET implementation represented by the XML Schema.

    [​IMG]

    The following conditions must be satisfied for the DataContractSerializer to serialize the types and exposing the contracts.
    1. It must contain the attributes as [ DataContract ] and [ DataMember ]
    2. The types attribute marked with the [ CollectionDataContract ]
    3. Derived from the interface IXmlSerializable
    4. The CLR built in primitive data types
    5. Arrays and collections used in the contract
    6. The enumerations used by the user
    7. The code containing the [ Serializable ] attribute and implementing the ISerializable
    The XML Schema for the NET class

    The [ DataContract ] attribute is defined in the System.Runtime.Serialization namespace. It indicates that the class is exposed to the XSD. The class without the [ DataContract ] attribute is unavailable in the WSDL. The [ DataMember ] attribute is defined in the System.Runtime.Serialization namespace. The [ DataContract ] members are defined in the class are included in the schema.

    The following code demonstrates the class containing the data members.

    Code:
    using System;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    
    namespace DefineWCF
    {
        [ DataContract ( Namespace =”http://DefineWCF”, Name=”Value” ) ]
        public class Rate
        {
            [ DataMember ( Name=”Product”, SequenceNo=0, IsRequired=true) ]
            public string ProductName;
        
            [ DataMember ( Name=”Quantity”, SequenceNo=1, IsRequired=true) ]
            public double QuantityNo;
    
            [ DataMember ( Name=”UnitPrice”, SequenceNo=3, IsRequired=true) ]
            public double UnitPriceValue;
        
            [ DataMember ( Name=”Cost”, SequenceNo=4, IsRequired=true) ]
            public double TotalCost;
            
        }
    
        [ ServiceContract ]
        public class Service1
        {
            [ OperationContract ]
            Rate GetData ()
            {
                Rate r = new Rate ();
                r.ProductName=”Pens”;
                r.QuantityNo=100;
                r.UnitPrice=10;
                r.TotalCost=1000;
            }
        }
    }
    
    
    The XSD generated document for the above code is as shown below:
    Code:
    <? xml version=”1.0” encoding=”utf-8” ?>
    <xs:schema xmlns:tns=”http://DefineWCF” elementFormDefualt=”qualified”
    targetNamespace=”http://DefineWCF” xmlns:xs=”http://www.w3.org/2001/XMLSchema” >
    <xs:complexType name=”Rate”>
        <xs:Sequence>
            <xs: element name=”ProductName” type=”xs:string” />
            <xs: element name=”QuantityNo” type=”xs:double” minOccurs=”0” />
            <xs: element name=”UnitPrice” type=”xs:double” minOccurs=”0” />
            <xs: element name=”TotalCost” type=”xs:double” minOcurs=”0” />
        </xs:Sequence>
    </xs:complexType>
    <xs: element name=”Rate” type=”tns:Rate” />
    
    Class Hierarchies in WCF

    The code of a class contains the complex types for implementation. The concept of inheritance is implemented for representing the constructs. The concept of cost can be further distinguished as ‘StockPrice’ or ‘SamplePrice’. The WCF service supports class hierarchies and represents them in the WSDL. It serializes and deserializes the class structure and XML.

    In the below code, the class Rate is defined as two elements and a subclass as TotalRate which inherits from the class Rate.
    Code:
    [ DataContract ( Namespace=”http://DefineWCF/Rate/” ) ]
    public class Rate
    {
        [ DataMember]
        public double QuantityPrice;
        
        [DataMember]
        public double UnitPrice;
    
    }
    
    [ DataContract ( Namespace=”http://DefineWCF/TotalRate/” ) ]
    public class TotalRate: Rate
    {
        [ DataMember]
        public double TotalCost;
        
        [DataMember]
        public string ProductName;
    
    }
    
    The two schemas are supported in the hierarchy are as mentioned below:
    Code:
    <? xml version= “1.0” encoding=”utf-8” ?>
    <xs: schema xmlns:tns=”http://DefineWCF/Rate/” elementFormDefault=”qualified”
    targetNamespace=”http://DefineWCF/Rate/” xmlns:xs=”http://www.w3.org/2001/XMLSchema” >
    
    <xs: complexType name=”Rate”>
        <xs: sequence>
        <xs: element minOccurs=”0” name=”QuantityPrice” nullable=”true” type=”xs:double” />
        <xs: element minOccurs=”0” name=”UnitPrice” type=”xs:double” />
        </xs: sequence>
    </xs: complexType>
    <xs: element name=”Rate” nullable=”true” type=”tns:Rate” />
    </xs:schema>
    
    <?xml version=”1.0” encoding=”utf-8” ?>
    <xs: schema xmlns:tns=”http://DefineWCF/TotalRate/” elementFormDefault=”qualified”
    targetNamespace=”http://DefineWCF/TotalRate/” xmlns:xs=”http://www.w3.org/2001/XMLSchema” >
    <xs: import namespace=”http://DefineWCF/Rate/” />
    
    <xs: complexType name=”TotalRate” >
        <xs: extension xmlns:q1=”http://DefineWCF/Rate/” base=”q1:Rate” >
        
        <xs: sequence>
        <xs: element minOccurs=”0” name=”TotalCost” type=”xs: double” />
        <xs: element minOccurs=”0” name=”ProductName” type=”xs:string” />
        </xs: sequence>
        </xs: extension>
    </xs: complexType>
    <xs: element name=”TotalRate” nullable=”true” type=”tns: TotalRate” />
    </xs: schema>
    
    Additional Types in WSDL

    The standard data types defined in the WSDL are explained in the above examples. But sometimes there is a requirement to add the user defined data type in the code.

    Consider an example where a serialized derives class comes at a endpoint and is expecting a serialized base class. The WCF is unaware of deserialize the class as the derived class was not the part of the contract. In such conditions, the user must inform the WCF about the WSDL contract.

    The KnownTypes are introduced in the WCF services. The attribute can be used in four ways by the user. The attribute can be added to the [ DataContract ] , [ ServiceContract ], [ OperationContract ] or by adding the reference to the assembly into the configuration.

    The following code snippet demonstrates the DataContract that defines the base class. It contains of the base class as Product and the derived classes as Suppliers and Orderdetails.
    Code:
    using System;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    
    namespace InfoWCF
    {
        [ DataContract ( Namespace =”http://InfoWCF/”) ]
        [ KnownType ( typeof ( Suppliers ) ) ]
        [ KnownType ( typeof ( Orderdetails ) ) ]
        public class Product 
        {
            [ DataMember ]
            public string ProductName;
    
            [ DataMember ]
            public double quantity;
        }
        
        [ DataContract ( Namespace =”http://InfoWCF/” ) ]
        public class Suppliers : Product
        {
            [ DataMember ]
            public string SupplierName;
    
            [ DataMember ]
            public string location;
    
        }
        
        [ DataContract ( Namespace =”http://InfoWCF/” ) ]
        public class Orderdetails : Product
        {
            [ DataMember ]
            public string status;
            
            [ DataMember ]
            public double cost;
        }
    
        [ ServiceBehavior ( Namespace=”http://InfoWCF/ProductData/” ) ]
        [ ServiceBehavior ( Namespace=”http://InfoWCF/ProductData/” ) ]
    
    public class ProductDetails
    {
        [ OperationContract ]
        private string GetDetails ( string id, string type )
        {
            if ( type.Contains ( “Suppliers” )
            {
                Suppliers s = new Suppliers ();
                s.ProductName=”Laptop”;
                s.quantity=200;
                s.SupplierName=id
                s.location=”UK”;
                return s;
            }
            if ( type.Contains ( “Orderdetails” )
            {
                Orderdetails o = new Orderdetails ();
                o.status=id;
                o.cost=20000;
                o.ProductName=”Laptop”;
                o.quantity=200;
                return o;
            }
        }
    }
    
    User can define the KnownType at the OperationContract with the [ ServiceKnownType ] attribute. The operation defined by the KnownType at the Operation level can be used by the derived classes.

    The disadvantage of defining the known types in the code, user needs to define all the derived type value at the compile time. If a new type is added, the user needs to recompile the code. The two methods can be used to overcome this problem.

    User can move the known types reference from code to the configuration and the information is added in the system.runtime.serialization section.
    The following code shows the configuration file for the above code.
    Code:
    <system.runtime.serialization>
        <dataContractSerializer>
        <declaredTypes>
            <add type = “InfoWCF.Product, ProductDetails, 
                    Version=1.0.0.0, Culture=neutral,
                    PublicKeyToken=null” >
            <knownType type=”InfoWCF.Suppliers, ProductDetails,
                    Version=1.0.0.0, Culture=neutral,
                    PublicKeyToken=null” />
            <knownType type=”InfoWCF.OrderDetails, ProductDetails,
                    Version=1.0.0.0, Culture=neutral,
                    PublicKeyToken=null” />
            </add>
        </declareTypes>
        </dataContractSerializer>
    </system.runtime.serialization>
    
    Another solution is to add the derived type in the contract is to create it at runtime. The constructor of both the [ KnownType ] and the [ ServiceKnownType ] attribute accepts the string parameter.

    The following code snippet demonstrates the code at the runtime.
    Code:
    [ DataContract ( Namespace = “http://InfoWCF/” ) ]
    [ KnownType ( “GetKnownTypes” ) ]
    public class ProductDetails
    {
        [ DataMember ]
    public string ProductName;
    
    [ DataMember ]
    public double quantity;
    
    
    static Type[ ] GetKnownTypes () 
        {
        return new Type [ ] { typeof ( Suppliers), typeof ( OrderDetails) };
        }
    }
    
    
    [ DataContract ( Namespace =”http://InfoWCF/” ) ]
    public class Suppliers : Product
    {
        [ DataMember ]
        public string SupplierName;
    
        [ DataMember ]
        public string location;
    
    }
        
    [ DataContract ( Namespace =”http://InfoWCF/” ) ]
    public class Orderdetails : Product
    {
        [ DataMember ]
        public string status;
            
        [ DataMember ]
        public double cost;
    }
    
    
    Data Contracts versioning

    The Data Contract contains members defined in it. User must create the versions of the data contract while adding the data in it. There are two types of changes for the data contract versioning. They are known as Breaking and Nonbreaking changes.

    Nonbreaking Changes

    The nonbreaking changes states that the compatibility of the clients will not be affected while modifying the data. The following types of changes will not break the compatibility.
    1. Addition of the new nonrequired data members
    2. Removal of the existing nonrequired data members
    User can create an old type from a new message by eliminating the new or unavailable non required components. User can create the new message from the old type.

    Breaking Changes

    User can change the attributes in a data contract for preserving the backward compatibility. If the user makes changes to the following data contract defined, the clients will not function correctly.
    1. The changes to the name or the namespace of the data contract
    2. Changing the data type of an existing data member
    3. Renaming the existing data member
    4. Adding the members using the IsRequired=true
    5. Removing the members with the IsRequired=true
    Consider the following example to demonstrate the changes in the data contract. The two data contracts are defined by the user.
    Code:
    [ DataContract ( Namespace=”http://InfoWCF” ) ]
    public class Product  // Example 1
    {
    [ DataMember ]
        public string ProductName;
    
        [ DataMember ]
        public double quantity;
    }
    }
    
    [ DataContract ( Namespace=”http://InfoWCF” ) ]
    public class Product     //Example 2
    {
    [ DataMember ]
        public string ProductName;
    
        [ DataMember ]
        public double Price;
    }
    }
    
    In the Example 1 and 2, the data member quantity is removed and the Price is added. The change is nonbreaking.

    Data Contract Equivalence

    If the user is using the WCF to expose the service, the svcutil.exe to build the proxy of the class to access the service, user is not concerned about the representation of the messages between the clients and the service.

    The Data Contract directs the WCF to serialize the .NET type into an XML Infoset and deserialize the XML Infoset to the .NET type. The Infoset is encoded as text or binary data used for the communication. The .NET types can be used in the code but it is unaware of the encoded representation.

    User can work with different types in the client versus in the service. There is such possibility if the client and the service are created by different users or if only one side is using the WCF. By controlling the names of the [ DataMember ] attribute, user can make them appear in XML representation.

    If the client and the service work in the equivalent XML representation, the WCF can deserialize the XML Infoset into different .NET types. If the two classes serialize into same XML schema, the data contracts representing those classes are equivalent.

    The following code shows two equivalent data contracts. The first contract is exposed to the service; the second class is described by the client. The two are equivalent and generate the XML Schema Definitions.
    Code:
    [ DataContract ( Namespace =”http://InfoWCF” ) ]
    public partial class ProductSvc
    {
        [ DataMember ]
        public string ProductName;
        
        [ DataMember ]
        public double quantity;
    
        [ DataMember ]
        public double Price;
    }
    
    [ DataContract ( Namespace =”http://InfoWCF” ) ]
    public partial class Product
    {
        [ DataMember ( Order = 3) ] public string ProductName;
        [ DataMember ( Order = 2) ] public double quantity;
        [ DataMember ( Order = 1, Name = “Currency” ) ] public double Price;
    }
    
    
    Working with collections in WCF

    The collections in .NET are combined by the benefits of memory allocation, enumeration, and navigation. To serialize the collection into XML, WCF considers it as arrays.

    The following code shows the service contract and the operation that uses the collection.
    Code:
    using System;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    using System.Collection.Generic;
    
    namespace InfoWCF
    {
        [ DataContract ( Namespace =”http://InfoWCF” ) ]
        public class Product
        {
            [ DataMember ]
            public string ProductName;
    
            [ DataMember ]
            public double quantity;
        }
    
        [ CollectionDataContract ]
        public class ProductCollection : List < Product >
        {
        }
        
        [ ServiceContract ]
        public class ProductData
        {
    
            [ OperationContract ]
            private ProductDataCollection ( string [ ] item )
            {
                ProductDataCollection list = new ProductDataCollection ();
                for ( int i =0; i<item.GetUpperBound ( 0 ) + 1; i++ )
                {
                    Product p = new Product ();
                    p.ProductName = “Laptop”;
                    p.quantity = item [ i ];
                }
                return list;
            }
        }
    }
    
    In the above code, the attribute directs WCF to the serialize to the type supported by the IEnumerable. The ProductDataCollection class inherits from the List generic, which implements the base ICollection interface for enabling the serialization.
     
    Last edited by a moderator: Jan 21, 2017

Share This Page