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
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 Types = User 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;
private string Phone;
[DataMember(Name =
"Phone")]
private string Telephone;
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.
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
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:
The null value for
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;
}
Good to show some samples
ReplyDeleteReally Really....Great work on every article done.....excepting more articles on asp.net mvc and wcf
ReplyDeletenow onwords i am regular reader of paradise-coding-blogspot.in
thanks a lot
kasi
Plenty of information, nicely listed!
ReplyDelete