WCF C4 2012 Data Contract
WCF C4 2012 Data Contract
WCF:
Serializare
DataContract
Derivarea in WCF
CollectionDataContract
Bibliografie
Juval Lowy : Programming WFC Services - O’REILLY
Ioan Asiminoaei
WCF – Contracte de date 2/36
Contracte de date
Serializare
Contractul de date este parte a operatiilor contractuale pe care le suporta serviciul, la fel
cum contractul serviciului este parte a contractului.
Nu se poate transmite cod sau parti logice ale invocarilor de metode de pe partea de client
catre serviciu. Ceea ce se transmite este starea unui obiect si pornind de aici serviciul
si/sau clientul vor reconstrui obiectul.
.NET preia valorile fiecarui camp al unui obiect si le serializeaza in memorie sau in fisier
sau peste o conexiune in retea. Pentru deserializare, .NET creaza un nou obiect de tipul
dat, citeste valorile persistente si seteaza campurile cu aceste valori, folosind din nou
refelection. (Reflection poate accesa campurile private cat si cele din clasa de baza.)
[AttributeUsage(AttributeTargets.Delegate|
AttributeTargets.Enum |
AttributeTargets.Struct |
AttributeTargets.Class,
Inherited=false)]
public sealed class SerializableAttribute : Attribute
{}
Exemplu
[Serializable]
public class MyClass
Ioan Asiminoaei
WCF – Contracte de date 3/36
{...}
Adnotand astfel un tip, inseamna ca toti membrii tipului trebuie sa fie serializabili.
Daca dorim ca un anumit membru sa nu fie serializabil (chiar daca poate fi serializat)
adnotam acel membru cu atributul [NonSerialized].
[Serializable]
public class MyClass
{
[NonSerialized]
MyOtherClass m_OtherClass;
/* Methods and properties */
[NonSerialized]
string strInfo; // desi string poate fi serializat in .NET
}
Ioan Asiminoaei
WCF – Contracte de date 4/36
Folosirea unui Stream este iarasi o problema in acest scenariu deorece serverul si clientii
trebuie sa partajeze stream-ul
Observatie
Ioan Asiminoaei
WCF – Contracte de date 5/36
Exemplu cu DataContractSerializer
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.ServiceModel;
namespace DataContractSerialization
{
class Program
{
static void Main(string[] args)
{
Student student = new Student()
{
Age = 12,
Name = "David"
};
DataContractSerializer dcs =
new DataContractSerializer(typeof(Student));
StringBuilder sb = new StringBuilder();
/*
public static XmlWriter Create(StringBuilder output)
Parameters
output
Type: System.Text.StringBuilder
The StringBuilder to which to write to. Content written
by the XmlWriter is appended to the StringBuilder.
Return Value
Type: System.Xml.XmlWriter
An XmlWriter object.
*/
Console.WriteLine(xml);
Ioan Asiminoaei
WCF – Contracte de date 6/36
Console.WriteLine(
"Obiectul construit dupa deserializare");
Console.WriteLine("newStudent : Name = {0}, Age = {1}",
newStudent.Name, newStudent.Age);
}
Console.ReadKey();
}
}
[DataContract(Namespace = "http://www.netspecial.com")]
public class Student
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
}
Rezultatul este :
<?xml version="1.0" encoding="utf-16"?>
<Student xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.netspecial.com">
<Age>12</Age>
<Name>David</Name>
</Student>
using System;
using System.IO;
using System.Xml;
using System.Text;
try
{
Ioan Asiminoaei
WCF – Contracte de date 7/36
writer.Flush();
}
finally
{
if (writer != null)
writer.Close();
}
}
}
<book>
<item>tesing</item>
</book>
Ioan Asiminoaei
WCF – Contracte de date 8/36
Exemplu
[Serializable]
struct Contact
{
public string FirstName;
public string LastName;
}
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
[Serializable]
struct Contact
{
public string FirstName;
public string LastName;
[NonSerialized]
public string Address;
}
Ioan Asiminoaei
WCF – Contracte de date 9/36
Atributul DataContract
[AttributeUsage(AttributeTargets.Enum |
AttributeTargets.Struct|
AttributeTargets.Class,
Inherited = false)]
public sealed class DataContractAttribute : Attribute
{
public string Name
{get;set;}
public string Namespace
{get;set;}
}
Aplicand acest atribut unei clase sau structuri nu este suficient pentru ca WCF sa
serializeze membrii sai.
[DataContract]
struct Contact
{
//Nu vor fi parte a contractului de date
public string FirstName;
public string LastName;
}
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,
Inherited = false)]
public sealed class DataMemberAttribute : Attribute
{
public bool IsRequired
{get;set;}
public string Name
{get;set;}
public int Order
{get;set;}
}
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;
[DataMember]
Ioan Asiminoaei
WCF – Contracte de date 10/36
sau proprietati:
[DataContract]
struct Contact
{
string m_FirstName;
string m_LastName;
[DataMember]
public string FirstName
{
get
{...}
set
{...}
}
[DataMember]
public string LastName
{
get
{...}
set
{...}
}
}
[DataMember]
string m_LastName;
}
Ioan Asiminoaei
WCF – Contracte de date 11/36
De exemplu, pentru:
namespace MyNamespace
{
[DataContract]
struct Contact
{...}
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
}
[OperationContract]
Contact[] GetContacts( );
}
Se poate furniza un alt spatiu de nume prin atribuirea unei valori proprietatii Namespace
din atributul DataContract.
namespace MyNamespace
{
[DataContract(Namespace = "MyOtherNamespace")]
struct Contact
Ioan Asiminoaei
WCF – Contracte de date 12/36
{...}
}
namespace MyOtherNamespace
{
[DataContract]
struct Contact
{...}
}
[DataMember]
public string LastName;
}
[DataMember]
public string FirstName
{
get
{
return FirstNameField;
}
set
{
FirstNameField = value;
}
}
[DataMember]
public string LastName
{
get
{
return LastNameField;
}
set
Ioan Asiminoaei
WCF – Contracte de date 13/36
{
LastNameField = value;
}
}
}
Observatie
Se poate modifica aceasta definitie pe partea de client (manual).
[DataContract]
struct Contact
{
[DataMember]
string FirstName
{get;set;}
[DataMember]
string LastName;
}
Daca atributul DataMember este aplicat unei proprietati pe partea de server, definitia
importata va fi tot o proprietate si va avea numele proprietatii din server.
[DataContract]
public partial struct Contact
{
string m_FirstName;
string m_LastName;
[DataMember]
public string FirstName
{
get
{
return m_FirstName;
}
set
{
m_FirstName = value;
}
}
[DataMember]
public string LastName
{
get
{
return m_LastName;
}
set
{
Ioan Asiminoaei
WCF – Contracte de date 14/36
m_LastName = value;
}
}
}
[DataMember]
public string FirstName
{
get
{
return FirstNameField;
}
set
{
FirstNameField = value;
}
}
[DataMember]
public string LastName
{
get
{
return LastNameField;
}
set
{
LastNameField = value;
}
}
}
Ioan Asiminoaei
WCF – Contracte de date 15/36
[Serializable]
struct Contact
{
string m_FirstName;
public string LastName;
}
Cand se importa metadata pentru acest tip, definitia importata va folosi atributul
DataContract.
In acest caz fiecare camp care este serializabil va fi privit ca un camp adnotat cu
DataMember.
[DataContract]
struct Address
{
[DataMember]
public string Street;
[DataMember]
public string City;
[DataMember]
public string State;
[DataMember]
public string Zip;
}
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;
Ioan Asiminoaei
WCF – Contracte de date 16/36
[DataMember]
public string LastName;
[DataMember]
public Address Address;
}
Cand se publica un contract de date compus, toate datele contractului vor fi publicate.
Exemplu :
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
si atributele corespunzatoare.
[DataContract]
class MyDataContract
{
[OnSerializing]
void OnSerializing(StreamingContext context)
{...}
[OnSerialized]
void OnSerialized(StreamingContext context)
{...}
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{...}
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{...}
//Data members
Ioan Asiminoaei
WCF – Contracte de date 17/36
namespace StreamingContextExample
{
class Program
{
static void Main(string[] args)
{
try
{
SerializeAndDeserialize();
}
catch (System.Exception exc)
{
Console.WriteLine(exc.Message);
}
finally
{
Console.WriteLine("Press <Enter> to exit....");
Console.ReadLine();
}
}
static void SerializeAndDeserialize()
{
object myObject = DateTime.Now;
Ioan Asiminoaei
WCF – Contracte de date 18/36
[Serializable]
[SecurityPermission(SecurityAction.Demand,
SerializationFormatter = true)]
class MyClass : ISerializable
{
private int minValue_value;
private int maxValue_value;
public MyClass()
{
minValue_value = int.MinValue;
maxValue_value = int.MaxValue;
}
Ioan Asiminoaei
WCF – Contracte de date 19/36
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
m_Connection = new SqlConnection(...);
}
/* Data members */
}
[DataMember]
public string LastName;
}
[DataContract]
class Customer : Contact
{
[DataMember]
public int OrderNumber;
}
Ioan Asiminoaei
WCF – Contracte de date 20/36
Exemplu
[DataContract]
class Customer : Contact
{
[DataMember]
public int OrderNumber;
}
[ServiceContract]
interface IContactManager
{
// Aici nu se accepta un obiect Customer
[OperationContract]
void AddContact(Contact contact);
Solutia consta in a spune WCF-ului in mod explicit ce inseamna clasa Customer folosind
atributul KnownTypeAttribute definit astfel:
[AttributeUsage(AttributeTargets.Struct|AttributeTargets.Class,
AllowMultiple = true)]
public sealed class KnownTypeAttribute : Attribute
{
public KnownTypeAttribute(Type type);
//More members
}
[DataContract]
[KnownType(typeof(Customer))]
class Contact
{...}
[DataContract]
class Customer : Contact
{...}
Ioan Asiminoaei
WCF – Contracte de date 21/36
Atributul ServiceKnownTypeAttribute
Acest atribut se aplica operatiilor si indica ca numai acele operatii pot accepta subclase.
[AttributeUsage(AttributeTargets.Interface|
AttributeTargets.Method |
AttributeTargets.Class,
AllowMultiple = true)]
public sealed class ServiceKnownTypeAttribute : Attribute
{
public ServiceKnownTypeAttribute(Type type);
//More members
}
Exemplu:
[DataContract]
class Contact
{...}
[DataContract]
class Customer : Contact
{...}
[ServiceContract]
interface IContactManager
{
[OperationContract]
[ServiceKnownType(typeof(Customer))]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
Cand atributul se aplica la nivel de contract, toate operatiile din acel contract pot accepta
subclase.
[ServiceContract]
[ServiceKnownType(typeof(Customer))]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
Ioan Asiminoaei
WCF – Contracte de date 22/36
[OperationContract]
Contact[] GetContacts( );
}
Observatie
Nu trebuie aplicat acest atribut la nivel de serviciu pentru ca nu are efect asupra
contractelor de date definite separat.
[DataContract]
class Contact
{...}
[DataContract]
class Customer : Contact
{...}
[DataContract]
class Person : Contact
{...}
[ServiceContract]
[ServiceKnownType(typeof(Customer))]
[ServiceKnownType(typeof(Person))]
interface IContactManager
{...}
[DataContract]
class Customer : Contact
{...}
[DataContract]
class Person : Customer
{...}
[ServiceContract]
[ServiceKnownType(typeof(Customer))]
[ServiceKnownType(typeof(Person))]
interface IContactManager
{...}
Ioan Asiminoaei
WCF – Contracte de date 23/36
Configurare
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type = "Contact,Host,Version=1.0.0.0,Culture=neutral,
PublicKeyToken=null">
<knownType type = "Customer,MyClassLibrary,Version=1.0.0.0,
Culture=neutral,PublicKeyToken=null"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Observatie
Daca un tip este intern in alt assembly, fisierele de configurare constituie singura
modalitate de a le face cunoscute in server si client.
Object si Interfete
Tipul de baza al unui contract poate fi o interfata:
interface IContact
{
string FirstName
{get;set;}
string LastName
{get;set;}
}
[DataContract]
class Contact : IContact
{...}
Se poate folosi aceasta interfata in contractul de serviciu sau ca data membru in contracte
de date atata timp cat folosim atributul ServiceKnownType in proiectarea tipului de
data:
[ServiceContract]
[ServiceKnownType(typeof(Contact))]
interface IContactManager
{
[OperationContract]
void AddContact(IContact contact);
[OperationContract]
IContact[] GetContacts( );
}
Ioan Asiminoaei
WCF – Contracte de date 24/36
//Imported definitions:
[DataContract]
class Contact
{...}
[ServiceContract]
public interface IContactManager
{
[OperationContract]
[ServiceKnownType(typeof(Contact))]
[ServiceKnownType(typeof(object[]))]
void AddContact(object contact);
[OperationContract]
[ServiceKnownType(typeof(Contact))]
[ServiceKnownType(typeof(object[]))]
object[] GetContacts( );
}
[DataContract]
class Contact
{...}
[ServiceContract]
public interface IContactManager
{
[OperationContract]
[ServiceKnownType(typeof(Contact))]
void AddContact(object contact);
[OperationContract]
[ServiceKnownType(typeof(Contact))]
object[] GetContacts( );
}
Daca avem definitia interfetei pe partea de client atunci o putem folosi in locul lui object.
[DataContract]
class Contact : IContact
{...}
Ioan Asiminoaei
WCF – Contracte de date 25/36
[ServiceContract]
public interface IContactManager
{
[OperationContract]
[ServiceKnownType(typeof(Contact))]
void AddContact(IContact contact);
[OperationContract]
[ServiceKnownType(typeof(Contact))]
IContact[] GetContacts( );
}
“Required members”
[DataMember(IsRequired=true)]
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
[DataMember(IsRequired = true)]
public string Address;
}
Cand sunt exportate tipuri custom serializabile, toti membrii tipului sunt adnotati cu
[DataMember(IsRequired=true)].
[Serializable]
struct Contact
{
public string FirstName;
public string LastName;
}
si metadata este:
[DataContract]
struct Contact
{
[DataMember(IsRequired = true)]
public string FirstName
Ioan Asiminoaei
WCF – Contracte de date 26/36
{get;set;}
[DataMember(IsRequired = true)]
public string LastName
{get;set;}
}
[OptionalField]
public string LastName;
}
[DataMember]
public string LastName
{get;set;}
}
Ioan Asiminoaei
WCF – Contracte de date 27/36
Enumerari
Enumerarile sunt totdeauna serializabile. Nu trebuie aplicat atributul [DataContract].
Toate valorile din enum vor fi incluse in mod implicit in contract.
enum ContactType
{
Customer,
Vendor,
Partner
}
[DataContract]
struct Contact
{
[DataMember]
public ContactType ContactType;
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
Daca se doreste ca numai anumite valori sa fie prinse in contract se foloseste atributul
[EnumMember] pe valoarea respectiva.
[DataContract]
enum ContactType
{
[EnumMember]
Customer,
[EnumMember]
Vendor,
// Nu va fi parte a contractului
Partner
}
Ioan Asiminoaei
WCF – Contracte de date 28/36
{
[EnumMember(Value = "MyCustomer")]
Customer,
[EnumMember]
Vendor,
[EnumMember]
Partner
}
si va reprezenta in fapt:
enum ContactType
{
MyCustomer,
Vendor,
Partner
}
DataSet si DataTable
DataRow nu e serializabil.
[Serializable]
public class DataSet : ...
{...}
[Serializable]
public class DataTable : ...
{...}
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
void AddContacts(DataTable contacts);
[OperationContract]
DataTable GetContacts( );
}
Ioan Asiminoaei
WCF – Contracte de date 29/36
Cand se importa definitia pentru contractul de mai sus, proxy va contine definitia pentru
DataTable.
Array contine object, deci putem pune orice avand grija la conversii. Se poate scrie o
metoda ce transforma multimea inregistrarilor unei tabele intr-un array (Incercati!).
Colectii
Colectia = tip ce suporta interfetele IEnumerable sau IEnumerable<T>.
Un contract de date poate include o colectie ca date membru, sau un contract de serviciu
poate defini operatii ce intercationeza cu colectia in mod direct.
IEnumerable<T> ;
IList<T> ;
ICollection<T>.
Exemplu :
[ServiceContract]
interface IContactManager
{
[OperationContract]
IEnumerable<Contact> GetContacts( );
...
}
class ContactManager : IContactManager
{
List<Contact> m_Contacts = new List<Contact>( );
va fi exportat ca:
[ServiceContract]
interface IContactManager
Ioan Asiminoaei
WCF – Contracte de date 30/36
{
[OperationContract]
Contact[] GetContacts( );
}
Exemplu
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
List<Contact> GetContacts( );
}
[OperationContract]
Contact[] GetContacts( );
}
Ioan Asiminoaei
WCF – Contracte de date 31/36
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
List<Contact> GetContacts( );
}
//Service implementation
class ContactManager : IContactManager
{
List<Contact> m_Contacts = new List<Contact>( );
[OperationContract]
Contact[] GetContacts( );
}
public partial class ContactManagerClient :
ClientBase<IContactManager>, IContactManager
{
public Contact[] GetContacts( )
{
return Channel.GetContacts( );
}
}
//Client code
ContactManagerClient proxy = new ContactManagerClient( );
Contact[] contacts = proxy.GetContacts( );
proxy.Close( );
Ioan Asiminoaei
WCF – Contracte de date 32/36
Colectii personalizate
Orice colectie personalizata poate fi transferata ca un array.
IEnumerator<T> IEnumerable<T>.GetEnumerator( )
{...}
//Rest of the implementation
}
[ServiceContract]
interface IMyContract
{
[OperationContract]
MyCollection<string> GetCollection( );
}
Acest atribut nu face colectia serializabila, doar o expune clientului ca o lista generica
inlantuita.
Ioan Asiminoaei
WCF – Contracte de date 33/36
Exemplu
[CollectionDataContract(Name = "MyCollectionOf{0}")]
public class MyCollection<T> : IEnumerable<T>
{
public void Add(T item)
{}
IEnumerator<T> IEnumerable<T>.GetEnumerator( )
{...}
//Rest of the implementation
}
si definitia in serviciu
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
MyCollection<Contact> GetContacts( );
}
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
MyCollectionOfContact GetContacts( );
}
Observatie
Ioan Asiminoaei
WCF – Contracte de date 34/36
Referentierea colectiei
SvcUtil.exe are switch /collectionType (sau /ct) pentru a permite sa facem referire la o
colectie particulara dintr-un assembly, pentru a extrage metadata pe partea de client.
Trebuie specificata locatia assembly-ului si bineinteles assembly trebuie sa fie disponibil
clientului.
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Stack<Contact> GetContacts( );
}
SvcUtil http://localhost:8000/
/r:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll
/ct:System.Collections.Generic.Stack'1
Rezultatul definitiei contractului pentru client va fi (se pastreaza definitia lui Stack<T>):
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Stack<Contact> GetContacts( );
}
Ioan Asiminoaei
WCF – Contracte de date 35/36
Dictionare
Dictionarele au propria reprezentare in WCF.
[Serializable]
public class MyDictionary : IDictionary
{...}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );
}
va fi expus astfel :
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
Dictionary<object,object> GetContacts( );
}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary<int,Contact> GetContacts( );
}
reprezentarea va fi ca Dictionary<K,T>:
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
Dictionary<int,Contact> GetContacts( );
}
Ioan Asiminoaei
WCF – Contracte de date 36/36
[CollectionDataContract]
public class MyDictionary : IDictionary
{...}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );
}
va avea reprezentarea
[CollectionDataContract]
public class MyDictionary : Dictionary<object,object>
{}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );
}
[CollectionDataContract]
public class MyDictionary<K,T> : IDictionary<K,T>
{...}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary<int,Contact> GetContacts( );
}
[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );}
Ioan Asiminoaei