0% au considerat acest document util (0 voturi)
44 vizualizări36 pagini

WCF C4 2012 Data Contract

Încărcat de

Sabina Diac
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
44 vizualizări36 pagini

WCF C4 2012 Data Contract

Încărcat de

Sabina Diac
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 36

WCF – Contracte de date 1/36

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.

Contractul de date este publicat in metadata si permite clientilor de a converti


reprezentarea neutra a datelor intr-o reprezentare nativa (tipuri CLR .NET).

Obiectele locale si referintele la acestea constituie concepte ale CLR.

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.

Starea obiectului se transmite prin ceea ce se numeste serializare si se construieste in


timpul operatiei de deserializare.

.NET serializeaza si deserializeaza obiectele folosind reflection (instrospectia


metadatei).

.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.)

Un stream este o secventa logica de « bytes », si nu depinde de mediul particular cum ar


fi fisier, memorie, port de comunicatie sau alta resursa.

Tipurile construite de utilizator (clase si structuri) nu sunt serializabile in mod implicit.


Dezvoltatorul tipului decide daca acesta poate fi serializat sau nu.
Pentru a indica ca un tip este serializabil se poate adnota tipul cu atributul
SerializableAttribute definit astfel :

[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].

public class MyOtherClass


{..}

[Serializable]
public class MyClass
{
[NonSerialized]
MyOtherClass m_OtherClass;
/* Methods and properties */

[NonSerialized]
string strInfo; // desi string poate fi serializat in .NET
}

Formate .NET pentru serializare


Exista doua formate:
 BinaryFormatter – serializare intr-o forma binara compacta, serializare rapida.
 SoapFormatter – foloseste formatul XML SOAP specific pentru .NET.

Ambele formate suporta interfata IFormatter, definita astfel:

public interface IFormatter


{
object Deserialize(Stream serializationStream);
void Serialize(Stream serializationStream,object graph);
// More members
}

public sealed class BinaryFormatter : IFormatter,...


{...}
public sealed class SoapFormatter : IFormatter,...
{...}

Interfata IFormatter defineste metodele Serialize si Deserialize, implementate de clasele


BinaryFormatter si SoapFormatter.

Indiferent de formatul folosit, ambele formate salveaza in stream si informatii despre


versionare si assembly-ul unde sunt definite tipurile. Din cauza ca se utilizeaza informatii
despre assembly, acest tip de serializare nu poate fi folosit in crearea serviciilor (clientii
si serverul trebuie sa aiba acelasi assembly)
.

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

Formate WCF pentru serializare

DataContractSerializer : definit in System.Runtime.Serialization

public abstract class XmlObjectSerializer


{
public virtual object ReadObject(Stream stream);
public virtual object ReadObject(XmlReader reader);
public virtual void WriteObject(XmlWriter writer,object graph);
public void WriteObject(Stream stream,object graph);
//More members
}
public sealed class DataContractSerializer : XmlObjectSerializer
{
public DataContractSerializer(Type type);
//More members
}

Metodele pentru serializare si deserializare sunt ReadObject, respectiv WriteObject.

DataContractSerializer nu suporta interfata IFormatter.

WCF foloseste in mod automat DataContractSerializer, iar ca dezvoltatori nu trebuie sa


scriem cod.

Observatie

Se poate folosi DataContractSerializer pentru a serializa tipuri in si din stream .NET,


iar in ctor va trebui sa furnizam tipul pe care dorim sa-l folosim.

MyClass obj1 = new MyClass( );


DataContractSerializer formatter =
new DataContractSerializer(typeof(MyClass));

using(Stream stream = new MemoryStream( ))


{
formatter.WriteObject(stream,obj1);
stream.Seek(0,SeekOrigin.Begin);
MyClass obj2 = (MyClass)formatter.ReadObject(stream);
}

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.

*/

using (XmlWriter writer = XmlWriter.Create(sb))


{
dcs.WriteObject(writer, student);
}
string xml = sb.ToString();

Console.WriteLine(xml);

using (XmlTextReader reader = new XmlTextReader(


new StringReader(xml)))
{
Student newStudent = (Student)dcs.ReadObject(
reader, true);

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>

Obiectul construit dupa deserializare

newStudent : Name = David, Age = 12

Cazul cand se foloseste un stream (MSDN)

using System;
using System.IO;
using System.Xml;
using System.Text;

public class Sample


{

public static void Main()


{

XmlWriter writer = null;

try
{

Ioan Asiminoaei
WCF – Contracte de date 7/36

// Create an XmlWriterSettings object with the correct


options.

XmlWriterSettings settings = new XmlWriterSettings();


settings.Indent = true;
settings.IndentChars = ("\t");
settings.OmitXmlDeclaration = true;

// Create the XmlWriter object and write some content.

writer = XmlWriter.Create("data.xml", settings);


writer.WriteStartElement("book");
writer.WriteElementString("item", "tesing");
writer.WriteEndElement();

writer.Flush();

}
finally
{
if (writer != null)
writer.Close();
}
}
}

iar fisierul data.xml va contine:

<book>

<item>tesing</item>

</book>

Ioan Asiminoaei
WCF – Contracte de date 8/36

Contract de date via serializare

Toate tipurile primitive construite in .NET sunt serializabile.

Pentru a putea folosi un tip construit de utilizator ca un parametru al unei operatii


trebuiesc indeplinite urmatoarele conditii:

 tipul trebuie sa fie serializabil;


 clientul si serverul trebuie sa aiba definitia locala a tipului.

Exemplu

[Serializable]
struct Contact
{
public string FirstName;
public string LastName;
}

[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
Contact[] GetContacts( );
}

Clientul poate utiliza o definitie echivalenta a tipului, ca in exemplul urmator:

[Serializable]
struct Contact
{
public string FirstName;
public string LastName;

[NonSerialized]
public string Address;
}

Observati data membru Address care nu este definita in contractul de baza.

Ioan Asiminoaei
WCF – Contracte de date 9/36

Atributul DataContract

DataContractAttribute este definit in spatiul de nume


System.Runtime.Serialization astfel:

[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;
}

Fiecare camp va fi membru al contractului de date daca este adnotat cu atributul


DataMemberAttribute, definit astfel :

[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;}
}

Acest atribut se poate aplica pe campuri:

[DataContract]
struct Contact
{
[DataMember]
public string FirstName;

[DataMember]

Ioan Asiminoaei
WCF – Contracte de date 10/36

public string LastName;


}

sau proprietati:

[DataContract]
struct Contact
{
string m_FirstName;
string m_LastName;

[DataMember]
public string FirstName
{
get
{...}
set
{...}
}

[DataMember]
public string LastName
{
get
{...}
set
{...}
}
}

Nu e necesar ca modificatorul de acces sa fie public.


[DataContract]
struct Contact
{
[DataMember]
string m_FirstName;

[DataMember]
string m_LastName;
}

Contractele de date sunt case sensitive, la nivel de tip si membru.

Ioan Asiminoaei
WCF – Contracte de date 11/36

Import contract de date


Cand un contract de date este folosit intr-o operatie a contractului, acesta este publicat in
metadata. Cand clientul importa definitia contractului de date, acesta va primi o definitie
echivalenta cu cea din server, dar nu identica.

Se mentine tipul original al clasei sau structurii si tipul spatiului de nume.

De exemplu, pentru:

namespace MyNamespace
{
[DataContract]
struct Contact
{...}

[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
Contact[] GetContacts( );
}
}

definitia importata va fi:


namespace MyNamespace
{
[DataContract]
struct Contact
{...}
}
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[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

{...}
}

Definitia importata va fi:

namespace MyOtherNamespace
{
[DataContract]
struct Contact
{...}
}

Definitia importata va avea totdeauna proprietatile adnotate cu atributul DataMember,


chiar daca tipul original de pe partea de serviciu nu defineste proprietati.
Campurile importate vor fi sufixate cu cuvantul Field.
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;

[DataMember]
public string LastName;
}

iar ceea ce se importa in client va fi:


[DataContract]
public partial struct Contact
{
string FirstNameField;
string LastNameField;

[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).

Modificatorul de acces al campurilor importate va fi totdeauna public, indiferent de cum


sunt definite pe partea de server.

[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;
}
}
}

iar definitia importata va fi:


[DataContract]
public partial struct Contact
{
string FirstNameField;
string LastNameField;

[DataMember]
public string FirstName
{
get
{
return FirstNameField;
}
set
{
FirstNameField = value;
}
}

[DataMember]
public string LastName
{
get
{
return LastNameField;
}
set
{
LastNameField = value;
}
}
}

Atributul DataMember se aplica la o propietate Read/Write, in caz contrar se va genera


exceptia InvalidDataContractException.

Nu trebuie aplicat atributul DataMember pe o proprietate si pe campurile private ce


compun acea proprietate.

Atributul DataMember se aplica atat pe partea de client cat si pe partea de serviciu.

Ioan Asiminoaei
WCF – Contracte de date 15/36

Contractul de date si atributul Serializable


Serviciul poate sa foloseasca un tip marcat numai cu atributul Serializable:

[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.

Un tip adnotat numai cu DataContract nu poate fi serializat; trebuie sa folosim si


atributul Serializable si DataMember.

Contracte de date compuse (agregate)


Cand se defineste un contract de date, trebuie aplicat atributul DataMember si pe
campurile care au aplicat atributul DataContract in definitia lor.

Atentie la structura Address de mai jos.

[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( );
}

va include definitia structurii Address.

Evenimente pe contractul de date


Pentru serializare si deserializare sunt definite urmatoarele evenimente:

 OnSerializing – inainte de ...


 OnSerialized – dupa ...
 OnDeserializing – inainte de ...
 OnDeserialized – dupa ...

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

Fiecare metoda ce trateaza aceste evenimente trebuie sa aiba prototipul:


void <Method Name>(StreamingContext context);

Daca atributele sunt aplicate pe metode cu prototipuri eronate, WCF genereaza o


exceptie.

StreamingContext este o structura ce descrie sursa si destinatia unui stream de


serializare dat si furnizeaza un context pentru apelant.

Exemplu din MSDN


using System;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;
using System.Security.Permissions;

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;

// Create a StreamingContext that includes a


// a DateTime.

StreamingContext sc = new StreamingContext(


StreamingContextStates.CrossProcess, myObject);
BinaryFormatter bf = new BinaryFormatter(null, sc);
MemoryStream ms = new MemoryStream(new byte[2048]);
bf.Serialize(ms, new MyClass());
ms.Seek(0, SeekOrigin.Begin);
MyClass f = (MyClass)bf.Deserialize(ms);

Ioan Asiminoaei
WCF – Contracte de date 18/36

Console.WriteLine("\t MinValue: {0} \n\t MaxValue: {1}",


f.MinValue , f.MaxValue);
Console.WriteLine("StreamingContext.State: {0}",
sc.State);

DateTime myDateTime = (DateTime)sc.Context;


Console.WriteLine("StreamingContext.Context: {0}",
myDateTime.ToLongTimeString());
}
}

[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;
}

public int MinValue


{
get { return minValue_value; }
set { minValue_value = value;}
}

public int MaxValue


{
get { return maxValue_value; }
set { maxValue_value = value; }
}

void ISerializable.GetObjectData(SerializationInfo si,


StreamingContext context)
{
si.AddValue("minValue", minValue_value);
si.AddValue("maxValue", maxValue_value);
}

protected MyClass(SerializationInfo si,


StreamingContext context)
{
minValue_value = (int)si.GetValue("minValue", typeof(int));
maxValue_value = (int)si.GetValue("maxValue", typeof(int));
}
}
}

Ioan Asiminoaei
WCF – Contracte de date 19/36

Exemplu de tratare eveniment pe deserializare terminata.


[DataContract]
class MyDataContract
{
IDbConnection m_Connection;

[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
m_Connection = new SqlConnection(...);
}
/* Data members */
}

Ierarhia contractului de date


In cazul unei ierarhii de clase folosite drept contracte de date, fiecare clasa trebuie
adnotata cu atributul DataContract, in caz contrar se genereaza o exceptie.
[DataContract]
class Contact
{
[DataMember]
public string FirstName;

[DataMember]
public string LastName;
}

[DataContract]
class Customer : Contact
{
[DataMember]
public int OrderNumber;
}

Cand se exporta o ierarhie de contracte de date, metadata contine ierarhia.

Ioan Asiminoaei
WCF – Contracte de date 20/36

Tipuri cunoscute. Atributul KnownTypeAttribute


In C# putem substitui o subclasa pentru o clasa de baza, in WCF nu.

Exemplu
[DataContract]
class Customer : Contact
{
[DataMember]
public int OrderNumber;
}

[ServiceContract]
interface IContactManager
{
// Aici nu se accepta un obiect Customer
[OperationContract]
void AddContact(Contact contact);

// Aici nu pot fi returnate obiecte Customer:


[OperationContract]
Contact[] GetContacts( );
}

Motivul este legat de serializare si deserializare. Nu se stie modul cum sa se contruiasca o


instanta pentru Customer.

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
}

Acest atribut ne permite sa proiectam subclase pentru contractul de date.

[DataContract]
[KnownType(typeof(Customer))]
class Contact
{...}

[DataContract]
class Customer : Contact
{...}

“Tipul Contact este un tip cunoscut de tipul Customer”.

Ioan Asiminoaei
WCF – Contracte de date 21/36

Pe partea de server, atributul KnownType afecteaza toate contractele si operatiile ce


folosesc clasa de baza si permite subclase in locul clasei de baza.

In metadata apare si subclasa.

Atributul ServiceKnownTypeAttribute

Acest atribut se aplica operatiilor si indica ca numai acele operatii pot accepta subclase.

Este definit astfel:

[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.

ServiceKnownType are proprietatea Multiple=true, deci poate fi aplicat de mai multe


ori pe aceeasi clasa.

[DataContract]
class Contact
{...}

[DataContract]
class Customer : Contact
{...}

[DataContract]
class Person : Contact
{...}

[ServiceContract]
[ServiceKnownType(typeof(Customer))]
[ServiceKnownType(typeof(Person))]
interface IContactManager
{...}

Nu functioneaza ceea ce stim de la mostenire. Trebuie sa adaugam tot lantul de derivare.


[DataContract]
class Contact
{...}

[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

Atributul KnownType nu poate fi aplicat pe interfete pentru ca interfetele nu sunt incluse


in metadata exportata.

Contractul exportat va fi bazat pe object si va include subclasa contractului de date sau


structura fara derivare:

//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( );
}

Definitia importata va avea ServiceKnownType aplicat la nivel de operatie, chiar daca la


creare a fost aplicat la nivel de contract.

Fiecare operatie va include o reuniune a tuturor atributelor ServiceKnownType cerute de


toate operatiile.

[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”

In informatia serializata / deserializata membrii respectivi trebuie sa existe.

[DataMember(IsRequired=true)]

Un membru al tipului adnotat cu DataMember poate sa nu existe in procesul de


serializare / deserializare – se considera valoarea implicita atribuita tipurilor preconstruite
sau null pentru tipuri referinta.

[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;}
}

Atributul [OptionalField] are ca efect export metadata fara IsRequired=true.


[Serializable]
struct Contact
{
public string FirstName;

[OptionalField]
public string LastName;
}

iar metadata este:


[DataContract]
struct Contact
{
[DataMember(IsRequired = true)]
public string FirstName
{get;set;}

[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.

Definitia atributului este:


[AttributeUsage(AttributeTargets.Field,Inherited = false)]
public sealed class EnumMemberAttribute : Attribute
{
public string Value
{get;set;}
}

[DataContract]
enum ContactType
{
[EnumMember]
Customer,

[EnumMember]
Vendor,

// Nu va fi parte a contractului
Partner
}

Alias pentru anumite valori:


[DataContract]
enum ContactType

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

DataSet si DataTable sunt definite ca tipuri serializabile in .NET.

DataRow nu e serializabil.

[Serializable]
public class DataSet : ...
{...}

[Serializable]
public class DataTable : ...
{...}

In concluzie putem scrie ceva de genul:


[DataContract]
struct Contact
{...}

[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 in loc de tabele

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.

WCF ofera reguli diferite pentru lucrul cu colectii.

Cand definim o operatie pe un serviciu ce foloseste urmatoarele interfete:

 IEnumerable<T> ;
 IList<T> ;
 ICollection<T>.

reprezentarea in retea va folosi totdeauna un array.

Exemplu :
[ServiceContract]
interface IContactManager
{
[OperationContract]
IEnumerable<Contact> GetContacts( );
...
}
class ContactManager : IContactManager
{
List<Contact> m_Contacts = new List<Contact>( );

public IEnumerable<Contact> GetContacts( )


{
return m_Contacts;
}
...
}

va fi exportat ca:

[ServiceContract]
interface IContactManager

Ioan Asiminoaei
WCF – Contracte de date 30/36

{
[OperationContract]
Contact[] GetContacts( );
}

Colectii concrete (nu o interfata)


Daca colectia din contract nu este o interfata si este o colectie serializabila (adnotata cu
atributul Serializable si nu cu DataContract), WCF poate normaliza colectia la un array
de tipul colectiei, furnizand metoda Add() cu una din semnaturile:
public void Add(object obj); //Collection uses IEnumerable
public void Add(T item); //Collection uses IEnumerable<T>

Exemplu
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
List<Contact> GetContacts( );
}

Clasa List este definita astfel:

public interface ICollection<T> : IEnumerable<T>


{...}
public interface IList<T> : ICollection<T>
{...}
[Serializable]
public class List<T> : IList<T>
{
public void Add(T item);
//More members
}

Pentru ca e o colectie valida si are metoda Add(), reprezentarea contractului va fi:


[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
Contact[] GetContacts( );
}

List<Contacts> este transferata drept Contact[].

Ioan Asiminoaei
WCF – Contracte de date 31/36

Serviciul poate sa returneze List<Contacts>, si clientul sa lucreze cu un array.


/////////////////////////// Service Side //////////////////////////////

[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
List<Contact> GetContacts( );
}

//Service implementation
class ContactManager : IContactManager
{
List<Contact> m_Contacts = new List<Contact>( );

public void AddContact(Contact contact)


{
m_Contacts.Add(contact);
}

public List<Contact> GetContacts( )


{
return m_Contacts;
}
}

/////////////////////////// Client Side //////////////////////////////


// proxy ...
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact 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.

Colectia trebuie sa fie serializabila.

/////////////////////////// Service Side //////////////////////////////


[Serializable]
public class MyCollection<T> : IEnumerable<T>
{
public void Add(T item)
{}

IEnumerator<T> IEnumerable<T>.GetEnumerator( )
{...}
//Rest of the implementation
}
[ServiceContract]
interface IMyContract
{
[OperationContract]
MyCollection<string> GetCollection( );
}

/////////////////////////// Client Side //////////////////////////////


[ServiceContract]
interface IMyContract
{
[OperationContract]
string[] GetCollection( );
}

Contract de date pentru colectii

Atributul CollectionDataContractAttribute definit astfel:


[AttributeUsage(AttributeTargets.Struct|AttributeTargets.Class,
Inherited = false)]
public sealed class CollectionDataContractAttribute : Attribute
{
public string Name
{get;set;}
public string Namespace
{get;set;}
//More members
}

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( );
}

Pe partea de client importul arata astfel:


[CollectionDataContract]
public class MyCollectionOfContact : List<Contact>
{}

[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
MyCollectionOfContact GetContacts( );
}

Observatie

La incarcarea serviciului se verifica prezenta metodei Add() si IEnumerable sau


IEnumerable<T>. Lipsa acestora va genera o exceptie InvalidDataContractException.

Nu se poate aplica DataContract si CollectionDataContract impreuna.

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.

De exemplu, serviciul ar putea defini urmatorul contract ce foloseste colectia Stack<T>:

[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);

[OperationContract]
Stack<Contact> GetContacts( );
}

Linia de comanda pentru SvcUtil.exe este:

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.

Daca dictionarul este o colectie serializabila ce suporta interfata IDictionary, atunci va fi


expus ca Dictionary<object, object>.

[Serializable]
public class MyDictionary : IDictionary
{...}

[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );
}

va fi expus astfel :

[ServiceContract]
interface IContactManager
{
...
[OperationContract]
Dictionary<object,object> GetContacts( );
}

Daca colectia serializabila suporta interfata IDictionary<K,T>:


[Serializable]
public class MyDictionary<K,T> : IDictionary<K,T>
{...}

[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

Daca folosim o colectie derivata din IDictionary, aceasta va fi transferata ca o subclasa a


respectivei reprezentari.

[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( );
}

in timp ce colectia generica:

[CollectionDataContract]
public class MyDictionary<K,T> : IDictionary<K,T>
{...}

[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary<int,Contact> GetContacts( );
}

va fi publicata in metadata ca:


[CollectionDataContract]
public class MyDictionary : Dictionary<int,Contact>
{}

[ServiceContract]
interface IContactManager
{
...
[OperationContract]
MyDictionary GetContacts( );}

Ioan Asiminoaei

S-ar putea să vă placă și