Tuesday, May 8, 2012

WCF-DataContract Basics


Data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged. 

DataContract is the default serialization programming model for WCF.

WCF  uses a serialization engine called the Data Contract Serializer by default to serialize and deserialize data and it comes unser namespace System.Runtime.Serialization
 

Types Supported by the Data Contract Serializer

Data contract types = These are types to which the DataContractAttribute attribute has been applied.
Collection types. = These are types that represent lists of data. Eg : Arrylist  and Dictionary 
Enumeration types =These can be marked with the DataContractAttribute attribute, in which every member must be marked with the EnumMemberAttribute attribute
Implicit Types =  Simple type such as int, string etc has an implicit data contract. These can be serialized with no other preparation and are considered as having default data contracts.
Explicit TypesUser defined object are,  for which you have to define a Data contract using [DataContract] and [DataMember] attribute.


DataContract Properties

Namespace : Used to get or set the namespace for the DataContract attribute
Name: Used to get or set the name for DataContract attribute.

DataMember Properties
           
Order : Used  to get or set the order of serialization and deserialization of a member
ISRequired: It instructs the serialization engine that member must be present while serialization or deserializing.
Name: Used  to gets or sets the DataMember name
EmitDefaultValue : It will specify whether the default value to be serialized.


Example 1 :

  [DataContract]
    public class FundsClass
    {
        [DataMember]
        public string FundName
        {
            get; set;
        }

        [DataMember]
        public int  FundID
        {
            get; set;
        }
    }


Example 2:

Service Contract sample

   [ServiceContract]
    public interface IService1Sample
    {
        // No data contract is required since parameter and return types are primitive types.
        [OperationContract]
         double GetPrice(int PriceID);

        // The Fundsdetail is a user-defined type thus requires a data contract.
        [OperationContract]
        string  CheckExistance(Fundsdetail ObjFund);

        [OperationContract]
        Fundsdetail getFunds();

        [OperationContract]
        Fundsdetail getFundName(int FundID);
    }

Data Contract  sample

    [DataContract]
    public class Fundsdetail
    {
        // This member is not serialized, but its property FundName is serialized.
        private string Fund_Name;

        // This member is not serialized, but its property FundID is serialized.
        private int Fund_ID;


        // This members is serialized
        [DataMember]
        public decimal PriceID;

        // This members is serialized
        [DataMember]
        private DateTime Pricedate;


        // This members is serialized
        [DataMember]
        public string FundName
        {
            // During serialization, the get code is called to get the value of the properties being serialized
            get
            {
                return Fund_Name;
            }
   // During deserialization, the set code is called to set the properties to the value being deserialized
            set
            {
                Fund_Name = value;
            }
        }

        // This members is serialized
        [DataMember]
        public int FundID
        {
            get { return Fund_ID; }
            set { Fund_ID = value; }
        }
     
    }


Service Example sample

  public class Service1Sample : IService1Sample
     {
         Fundsdetail _obj = new Fundsdetail();


         double GetPrice(int PriceID)
         {
             return 2.303;
         }
       
         // This methos return FundName .Logic can be changed with database connectivity to check either fund             exist or not
         public string CheckExistance(Fundsdetail ObjFund)
          {
              return ObjFund.FundName;
          }      
         public Fundsdetail getFunds()
         {
             //This information can also retrieved by database connectivity
             _obj.FundName = "Fund1";
             _obj.FundID = 1;
              //Price id properity is not detined in FundetailClass but it define as datamember
             // and Public so that here we can access this member
             // note that Price date is define as private in class so we can't access that member here
             _obj.PriceID = 120;
              return _obj;
         }

         Fundsdetail getFundName(int FundID)
         {
             //This information can be retrieved by database connectivity also
             _obj.FundName = "USGlobal Funds";
             return _obj;
         }

     }



Data Contracts and Circular References

IsReference = true property  capable of serializing object trees. Data Contracts serializer will generate XML elements with IDs/IDREFs attributes and will link them together rather embedding them inside each other

[DataContract(IsReference = true)]
    public class FundClass
    {
        [DataMember]
        public string FundName { get; set; }
        [DataMember]
        public FundPriceClass Funds { get; set; }

    }


Data Contract Names and namespace
1.      A  fully-qualified data contract name .
2.      Data members have only names, but no namespaces.
3.      When processing data contracts, It is case-sensitive to both the namespaces and the names of data contracts and data members.


[DataContract(Name = "BasicFundDetails", Namespace = "www.sample.com/samplecontracts")]
    public class FundDetail
    {
        string _FundName;
        string _FundID;

        // This data member is named PriceDate default
        [DataMember]
        private DateTime PriceDate;

              [DataMember(Name = "FundFullName")]
        public string FundName
        {
            get { return _FundName; }
            set { _FundName = value; }
        }

        [DataMember(Name = "BasicFundID")]
        public string FundID
        {
            get { return _FundID; }
            set { _FundID = value; }
        }
    }


Datamember order

  • In case of inheritance hierarchy, data members of its base types are always first in the order.
  • Next  data members that do not have the Order property  in alphabetical order.
  • Next are any data members that have the Order property  and then alphabetically



[DataContract(Name = "Employee")]
public class Person
{
    [DataMember(Order = 1)]
    private string PersonName;

    [DataMember(Order = 0)]
    private string Salary;

    [DataMember(Order = 0)]
    private string Address;

    [DataMember]
    private string PhNo;
}

//The default order is alphabetical so  Phno-Address-Salary-PersonName


Data Contract Equivalence

For data contracts to be equivalent,
  • they must have the same namespace and name.
  • Each data member on one side must have an equivalent data member on the other side.
  • data contract names and namespaces are case-sensitive.


For data members to be equivalent
  •  they must have the same name.
  •  they must represent the same type of data.
  •  data member names  are case-sensitive.

Eg 1.

[DataContract]
public class Employee
{
    [DataMember]
    public string FullName;

    [DataMember]
    public string Phone;
}

[DataContract(Name = "Employee")]
public class Person
{
    [DataMember(Name = "FullName")]
    private string PersonName;

    private string addressofEmp;

    [DataMember(Name = "Phone")]
    private string PhNo;
}




Data Member Order and Data Contract equivalence



    [DataContract(Name = "Parts")]
    public class Coords1
    {
        [DataMember]
        public int APart;
        [DataMember]
        public int Bpart;
        // Order is alphabetical (APart,Bpart).
    }

    [DataContract(Name = "Parts")]
    public class Coords2
    {
        [DataMember]
        public int Bpart;
        [DataMember]
        public int APart;      
        //Order is alphabetical (APart,Bpart).
     }

    [DataContract(Name = "Parts")]
    public class Coords3
    {
        [DataMember(Order = 2)]
        public int Bpart;
        [DataMember(Order = 1)]
        public int APart;

        // Order is according to the Order property (APart,Bpart).
      
    }


Data Contract Known Types

 The [KnownType] attribute is used to inform the deserialization engine about types that should be considered during the actual deserialization.

The attribute cannot be applied to individual data members, only to whole data contract types.
In object-oriented programming, a type that inherits from another type can be used in place of the original type

In service-oriented programming, schemas rather than types are communicated and therefore, the relationship between types is not preserved. The KnownTypeAttribute attribute allows information about derived types to be included in the data contract.

Known types can be associated only with classes and structures, not interfaces.
Eg 1 .
[DataContract]
    public class Customer {}

    [DataContract(Name = "Local")]
    public class LocalCustomer : Customer { }

    [DataContract(Name = "Other")]
    public class OtherCustomer : Customer { }

   
 // The following Category class can be serialized, but cannot be deserialized if the customer member is set to
    //either a Local or Other class, because the deserialization engine does not recognize any types
    //with data contract names "Manager" or "Developer":
    [DataContract]
    public class Category
    {
        [DataMember]
        private Customer customer;
    }

    //Correct Way
    [DataContract]
    [KnownType(typeof(LocalCustomer))]
    [KnownType(typeof(OtherCustomer))]
    public class Category
    {
        [DataMember]
        private Customer customer;
    }


Eg 2 :


    [DataContract]
    [KnownType(typeof(int[]))]
    [KnownType(typeof(ArrayList))]
    public class Operations
    {
        private object NumVal;
        [DataMember]
        public object Numbers_Value
        {
            get { return NumVal; }
            set { NumVal = value; }
        }

        public void Operate()
        {
            Operations md = new Operations();

            // This will serialize and deserialize successfully because primitive
            // types like int are always known.
            int a = 100;
            Numbers_Value = a;

            // This will serialize and deserialize successfully because the array of
            // integers was added to known types.
            int[] b = new int[100];
            md.Numbers_Value = b;

            // This will serialize and deserialize successfully because the generic
            // List<int> is equivalent to int[], which was added to known types.
            List<int> c = new List<int>();
            md.Numbers_Value = c;
            // This will serialize but will not deserialize successfully because
            // ArrayList is a non-generic collection
            // must be added to the known types.
            ArrayList d = new ArrayList();
            md.Numbers_Value = d;
        }





KnownTypeAttribute associate with base class is also associated with all of the derived types of that type.



// ********Class**********


// Base class         
[KnownType(typeof(FourWheelers))]
[KnownType(typeof(TwoWheelers))]
public class Motors
{
    [DataContract]
    public int MotorID { get; set; }
    //not set property defined
    abstract public int ProductionCountries { get; }

    [DataContract]
    public string MotorBrand { get; set; }
}


// FourWheelers class, inherits from Motors      
public class FourWheelers : Motors
{
    override public int ProductionCountries { get { return 4; } }

    public string SteeringWheelPosition { get; set; }
}


// TwoWheelers class, inherits from Motors        
[KnownType(typeof(ChildrenMotors))]
public class TwoWheelers : Motors
{
    override public int NoOfWheels { get { return 2; } }

    public bool HasFrontWheelBreak { get; set; }
}


// Childrenmotors class, inherits from TwoWheelers 

public class ChildrenMotors : TwoWheelers
{
    public bool HasSupportingWheels { get; set; }



// ********Interface**********
[ServiceContract]
public interface Imotorservices
{
    [OperationContract]
    Motors getmotor(int type);

    [OperationContract]
    int GetNumberOfCountries(Motors motor);
}

// ********Service**********

public class mot_Service : Imotorservices
{
    public Motors getmotor(int type)
    {
        switch (type)
        {
            case 0:
                return new FourWheelers()
                {
                    MotorID  = 100,
                    MotorBrand = "Nissan",
                    SteeringWheelPosition  = "left"                    
                };

            case 1:
                return new TwoWheelers()
                {
                    MotorID = 2311,
                    MotorBrand = "hero",
                    HasFrontWheelBreak = true
                };

            case 2:
                return new ChildrenMotors()
                {
                    MotorID  = 12,
                    MotorBrand = "Cycle",
                    HasFrontWheelBreak = false,
                    HasSupportingWheels = true
                };

            default:
                return null;
        }
    }
    public int GetNumberOfCountries(Motors  mot)
    {
        return mot.ProductionCountries;
    }
}  


Known Types Using Open Generic Methods

Write a method (must be static, must accept no parameters, and must return an object) that returns a list of types to add to the known types collection.
 The name of the method is then specified as a string argument to the KnownTypeAttribute attribute.

public class List1<T>{}

public class List2<T>{}

[DataContract]
[KnownType("TakeTypes")]
public class DrawingRecord2<T>
{
    [DataMember]
    private T TheData;
    private static Type[] TakeTypes()
    {
        Type[] t = new Type[2];
        t[0] = typeof(List1<T>);
        t[1] = typeof(List2<T>);
        return t;
    }
}




How to pass objects by references between server and client.

Very good example found in MSDN

Interface
    public interface ISocialNetwork
    {
        [OperationContract]
        List<Person> GetPeopleInNetwork(Person p);
        [OperationContract]
        List<Person> GetMutualFriends(Person p);
        [OperationContract]
        List<Person> GetCommonFriends(List<Person> p);
    }

Class
    [DataContract(IsReference=true)]
    public class Person
    {
        string name;
        string location;
        string gender;
        int age;
        List<Person> friends;
        [DataMember()]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        [DataMember()]
        public string Location
        {
            get { return location; }
            set { location = value; }
        }
        [DataMember()]
        public string Gender
        {
            get { return gender; }
            set { gender = value; }
        }
        [DataMember()]
        public int Age
        {
            get { return age; }
            set { age = value; }
        }
        [DataMember()]
        public List<Person> Friends
        {
            get { if (friends == null) friends = new List<Person>();  return friends; }
            set { friends = value; }
        }

        public Person()
        {
        }
    }


Service

    public class SocialNetworkService : ISocialNetwork
    {
        public List<Person> GetPeopleInNetwork(Person p)
        {
            List<Person> people = new List<Person>();
            ListPeopleInNetwork(p, people);
            return people;
           
        }
        public List<Person> GetMutualFriends(Person p)
        {
            List<Person> mutual = new List<Person>();
            foreach (Person friend in p.Friends)
            {
                if (friend.Friends.Contains(p))
                    mutual.Add(friend);
            }
            return mutual;
        }
        public List<Person> GetCommonFriends(List<Person> people)
        {
            List<Person> common = new List<Person>();
            foreach (Person friend in people[0].Friends)
                if (people[1].Friends.Contains(friend))
                    common.Add(friend);
            return common;
        }
       
        void ListPeopleInNetwork(Person p, List<Person> lst)
        {
            if (!lst.Contains(p))
            {
                lst.Add(p);
                foreach (Person friend in p.Friends)
                    ListPeopleInNetwork(friend, lst);
            }
        }
    }


Client

// Create a client
            SocialNetworkClient sn = new SocialNetworkClient();

            // Create my social network
            Person P1 = new Person() { Name = "name1", Age = 35, Location = "Canada" };
            Person P2 = new Person(){Name = "name2", Age=25, Location="Singapore"};
            Person P3 = new Person() { Name = "name3", Age = 28, Location = "Aus" };
            Person P4 = new Person() { Name = "name5", Age = 27, Location = "WE" };
            Person P5 = new Person() { Name = "name4", Age = 40, Location = "Slo" };

            helena.Friends = new Person[] { P1, P2, P3, P4 };
            andrew.Friends = new Person[] { P4, P3, P2 };
            paul.Friends = new Person[] { P5, P2 };
            sarah.Friends = new Person[] { P2 };
            richard.Friends = new Person[] { P1 };

            // Call the GetPeopleInNetwork to find people in Andrew's network
            Person[] network = sn.GetPeopleInNetwork(andrew);
            Console.WriteLine("Andrew's Network: ");
            foreach (Person p in network)
                Console.WriteLine(" "+p.Name);

            // Call GetMutualFriends to find Helena's mutual friends
            Person[] mutual = sn.GetMutualFriends(helena);
            Console.WriteLine("Helena's Mutual Friends: ");
            foreach (Person p in mutual)
                Console.WriteLine(" " + p.Name);

            // Call GetcommonFriends to find common friends of Helena and Andrew
            Person[] common = sn.GetCommonFriends(new Person[] {helena, andrew});
            Console.WriteLine("Helena and Andew's Common Friends: ");
            foreach (Person p in common)
                Console.WriteLine(" " + p.Name);

            Console.ReadLine();



Data Contract Versioning


Changes to a data contract can be breaking or nonbreaking.

Non Breaking : Both the old and the new versions of the data contract can still communicate
Breaking  : Stop communication in one or both directions
Following  changes are always breaking:
·         Changing the Name or Namespace value of a data contract.
·         Changing the order of data members by using the Order property of the DataMemberAttribute.
·         Renaming a data member.
·         Changing the data contract of a data member.
For example, changing the type of a data member from an integer to a string, or from a type with a data contract named "Customer" to a type with a data contract named "Person
Both  following members are same  either type changed

[DataMember]
    private string Phone;

[DataMember(Name = "Phone")]
    private string Telephone;



Data contract Control : 
If you decorate your code with DataContract, you have a lot of control about what the client can see and must send back to your service.
[DataContract]
public class SampleClass
{
    [DataMember(IsRequired=true)]
    public int MyRequiredProperty { get; set; } // must required property
    [DataMember]
    public int MyOptionalProperty { get; set; } // optional

    public int MyInternalProperty { get; set; } // Internal purpose not exposed to client
}



Round-Tripping

Round-tripping occurs when data passes from a new version to an old version and back to the new version of a data contract. 

Round-tripping guarantees that no data is lost. 

Enabling round-tripping makes the type forward-compatible with any future changes supported by the data contract versioning model.

To enable round-tripping for a particular type, the type must implement the IExtensibleDataObject interface. 

The interface contains one property, ExtensionData (returning the ExtensionDataObject type). 

The property stores any data from future versions of the data contract that is unknown to the current version.  


Version 1

[DataContract(Name = "Funds")]
class FundDetails : IExtensibleDataObject
{
    private ExtensionDataObject temp_value;
    public ExtensionDataObject ExtensionData
    {
        get
        {
            return temp_value; ;
        }
        set
        {
            temp_value = value;
        }
    }
    [DataMember]
    public string Name;
}

Version 2

[DataContract(Name = "Funds")]
class FundDetails : IExtensibleDataObject
{
    // Best practice: add an Order number to new members.
    [DataMember(Order = 2)]
    public int Fund_ID;

    private ExtensionDataObject temp_value;
    public ExtensionDataObject ExtensionData
    {
        get
        {
            return temp_value; ;
        }
        set
        {
            temp_value = value;
        }
    }
    [DataMember]
    public string Name;
}

The ExtensionDataObject type contains no public methods or properties. Thus, it is impossible to get direct access to the data stored inside the ExtensionData property.
 

Default Values  :
In the .NET Framework, types have a concept of default values.
 For example, for any reference type the default value is null, and for an integer type it is zero
If an instance of this class is serialized, the result is as follows: employeeName and employeeID is serialized. 

The null value for employeeName and the zero value for employeeID is explicitly part of the serialized data. However, the position, salary, and bonus members are not serialized. Finally, targetSalary is serialized as usual, even though the EmitDefaultValue property is set to false, because 57800 does not match the .NET default value for an integer, which is zero.
[DataContract]
public class Employee
{
    [DataMember]
    public string employeeName = null;
    [DataMember]
    public int employeeID = 0;
    [DataMember(EmitDefaultValue = false)]
    public string position = null;
    [DataMember(EmitDefaultValue = false)]
    public int salary = 0;
    [DataMember(EmitDefaultValue = false)]
    public int? bonus = null;
    [DataMember(EmitDefaultValue = false)]
    public int targetSalary = 57800;
}

3 comments :

  1. Good to show some samples

    ReplyDelete
  2. Really Really....Great work on every article done.....excepting more articles on asp.net mvc and wcf
    now onwords i am regular reader of paradise-coding-blogspot.in

    thanks a lot

    kasi

    ReplyDelete
  3. Plenty of information, nicely listed!

    ReplyDelete