BlogServiceHost.Create()

WCF & Azure - Il blog di Fabio Cozzolino

Recent Posts

Tags

My

Twitter

Community

Archives

Email Notifications

Social Bar Widget

February 2009 - Posts

[WCF] La serializzazione e i tipi "sconosciuti"

Immaginiamo di avere questo modello:

  1:     public class Person
  2:     {
  3:         public string FirstName { get; set; }
  4:         public string LastName { get; set; }
  5:     }
  6: 
  7:     public class Student : Person
  8:     {
  9:         public string StudentID { get; set; }
 10:     }
 11: 
 12:     public class Employee : Person
 13:     {
 14:         public string EmployeeID { get; set; }
 15:     }

Obiettivo del nostro servizio è quello di avere un'unica operation eseguita indipendentemente dal tipo specifico scambiato. Supponiamo quindi di avere questo contratto:

  1:     [ServiceContract]
  2:     public interface IPersonService
  3:     {
  4:         [OperationContract]
  5:         void Send(Person person);
  6:     }

Così com'è dichiarato ora, il contratto di servizio non ci consente di inviare i tipi Student e Employee. Sul client infatti avremo semplicemente la classe Person.

Per poter ottenere sul client anche le classi Student e Employee possiamo utilizzare l'attributo KnownType, contenuto nell'assembly System.Runtime.Serialization, da aggiungere alla classe Person:

  1:     [KnownType(typeof(Student))]
  2:     [KnownType(typeof(Employee))]
  3:     public class Person
  4:     {
  5:         public string FirstName { get; set; }
  6:         public string LastName { get; set; }
  7:     }

Magari definire il KnownType sulla classe Person non è proprio bello. Possiamo utilizzare in alternativa il ServiceKnownType. Stesso meccanismo ma questa volta l'attributo lo inseriamo sul servizio:

  1:     [ServiceContract]
  2:     [ServiceKnownType(typeof(Student))]
  3:     [ServiceKnownType(typeof(Employee))]
  4:     public interface IPersonService
  5:     {
  6:         [OperationContract]
  7:         void Send(Person person);
  8:     }

Meglio? In alcuni casi non è sufficiente. Possiamo sfruttare un'altro overload dell'attributo ServiceKnownType per farci restituire a runtime l'elenco dei tipi da uno specifico metodo:

  1:     [ServiceContract]
  2:     [ServiceKnownType("GetKnownTypes", typeof(KnownTypeHelper))]
  3:     public interface IPersonService
  4:     {
  5:         [OperationContract]
  6:         void Send(Person person);
  7:     }
  8: 
  9:     class KnownTypeHelper
 10:     {
 11:         public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
 12:         {
 13:             return new[]
 14:                        {
 15:                            typeof(Student), 
 16:                            typeof(Employee)
 17:                        };
 18:         }
 19:     }

Chiaramente nell'esempio ritorno un elenco fisso di tipi, ma questi potrebbero essere costruiti dinamicamente. Non sempre, infatti, siamo in grado di conoscere o comunque prevedere le evoluzioni future del nostro modello.

Cosa hanno in comune queste tre alternative. Tutte creano sul client i tipi Person, Student e Employee. Uno stralcio:

  1:     [DataContract(Name="Person", 
  2:         Namespace="http://schemas.datacontract.org/2004/07/ConsoleApplication1")]
  3:     [KnownType(typeof(Student))]
  4:     [KnownType(typeof(Employee))]
  5:     public partial class Person : object, IExtensibleDataObject
  6:     {
  7:     }
  8: 
  9:     [DataContract(Name="Student", 
 10:         Namespace="http://schemas.datacontract.org/2004/07/ConsoleApplication1")]
 11:     public partial class Student : Person
 12:     {
 13:     }
 14: 
 15:     [DataContract(Name="Employee", 
 16:         Namespace="http://schemas.datacontract.org/2004/07/ConsoleApplication1")]
 17:     public partial class Employee : Person
 18:     {
 19:     }
 20: 
 21:     [ServiceContract(ConfigurationName="IPersonService")]
 22:     public interface IPersonService
 23:     {   
 24:         [OperationContract(Action="http://tempuri.org/IPersonService/Send", 
 25:             ReplyAction="http://tempuri.org/IPersonService/SendResponse")]
 26:         void Send(ConsoleApplication1.Person person);
 27:     }

Esiste un'ultima alternativa. Il file di configurazione:

  1: <?xml version="1.0" encoding="utf-8" ?>
  2: <configuration>
  3:   <system.runtime.serialization>
  4:     <dataContractSerializer>
  5:       <declaredTypes>
  6:         <add type="Person">
  7:           <knownType type="Student" />
  8:           <knownType type="Employee" />
  9:         </add>
 10:       </declaredTypes>
 11:     </dataContractSerializer>
 12:   </system.runtime.serialization>
 13: </configuration>

Chiaramente il type nel file di configurazione deve comprendere il namespace e la definizione dell'assembly.

Il KnownType è un concetto legato alla serializzazione con il DataContractSerializer. Tra gli overloads disponibili abbiamo infatti la possibilità di utilizzare proprio un elenco di tipi:

  1:     IEnumerable<Type> knownTypes = new[] {typeof (Student), typeof (Employee)};
  2:     DataContractSerializer serializer = new DataContractSerializer(typeof(Person), knownTypes);
  3:     serializer.ReadObject(stream);

Il ServiceKnownType è invece strettamente legato all'utilizzo in WCF e può essere utilizzato solo in questi contesti. Dipedentemente dalla soluzione da adottare, quella che normalmente prediligo è l'utilizzo del ServiceKnownType con un metodo esterno dichiarato in una classe helper che fornisce l'elenco dei tipi. Mi trovo decisamente comodo. Non "sporco" più di tanto il servizio e mi garantisco la possibilità in futuro di estendere il tutto più facilmente.

Comunque le alternative sono diverse e potete scegliere tranquillamente la via che più vi piace.

bye

BASTA! Italia e .netSide

Nell’ultima newsletter inviata a tutti gli iscritti che ne hanno autorizzato la ricezione, abbiamo parlato dell’evento BASTA!Italia, la più importante conferenza tedesca su .NET, che sbarca per la prima volta in italia grazie alla preziosa collaborazione con Dino Esposito.

Gli organizzatori hanno voluto offrire a .netSide la possibilità di “regalare” un pass gratuito ai propri soci. Abbiamo quindi pensato di preparare un contest così da poter consegnare il pass a chi, nell’ultimo periodo, si è maggiormente distinto per contributi forniti alla community secondo la scala di priorità che riporto dalla newsletter:

  1. Scritto più articoli tecnici pubblicati su .netSide
  2. In caso di parità tra più utenti di articoli scritti il pass sarà attribuito al miglior articolo selezionato attraverso una votazione interna da parte dello staff di DotNetSide
  3. Ha aperto o mantiene un blog su dotnetside e scritto un minimo di 5 post tecnici* (credo che nell'arco di quasi 20 giorni sia un numero ragionevole)
  4. In caso di parità del numero di post tecnici scritti, il pass sarà attribuito a chi avrà raggiunto prima l'obiettivo
  5. Partecipazione nel forum con domande/risposte

* per post tecnici si intendono soluzioni a problematiche comuni o generalmente tecniche legate al mondo .NET. Verranno valutati solo i post che rientrano in questa categoria.

Un’occasione da non perdere e basta veramente poco!!!

Ma c’è di più. Se non volete partecipare al contest, invece, BASTA!Italia offre un ulteriore 10% di sconto fino al 27 Febbraio solo ai soci .netSide. Per ottenere lo sconto dovrete richiedere all’indirizzo staff@dotnetside.org il promotional code, inviando il vostro username su dotnetside.org, da inserire all’atto della registrazione.

Ancora una volta un’occasione da non perdere. Spero di vedervi lì… Smile

Posted: Feb 14 2009, 03:28 PM by Fabio.Cozzolino | with no comments
Filed under:
[OT] Questa volta la RAI una cosa buona l’ha fatta…

www.rai.tv è un portale che consente di guardare i canali Rai in diretta sul web, ma non solo. Se vi siete persi qualche puntata della vostra serie preferita oppure volete rivedere qualche vecchia serie…il portale rai.tv vi da anche questa opportunità.

Ah, dimenticavo… è tutto sviluppato utilizzando Silverlight Wink