BlogServiceHost.Create()

WCF & Azure - Il blog di Fabio Cozzolino

Recent Posts

Tags

My

Twitter

Community

Archives

Email Notifications

Social Bar Widget

Data Transfer Object, questo sconosciuto…

Molto spesso capitano domande, sui forum, via email o di persona, su come è possibile far viaggiare le proprie entity con WCF. Molti non sono disposti a marcare con gli attributi DataContract e DataMember, e direi anche giustamente. Il .NET Framework 3.5 consente anche di utilizzare oggetti senza la necessità di marcarli con gli appositi attributi.

Il punto però è un altro. Spesso la necessità non è quella di trasferire le entità, bensi un insieme probabilmente differente contenente informazioni specifiche e ben definite. Una ipotetica entità Book, nel nostro dominio, ha sicuramente un riferimento ad una entità Author. Probabilmente avremo qualcosa del tipo:

public class Book
{
    public string ISBN { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int Pages { get; set; }
    public double Price { get; set; }
    public Author Author { get; set; }
}
public class Author
{
    public string Name { get; set; }
}

Abbiamo semplificato parecchio. Se il nostro servizio esponesse l’entità Book così com’è saremmo costretti a generare un messaggio con dati probabilmente non necessari per lo specifico contesto. Perchè trasferire tutto? E se la gerarchia fosse ancora più complessa? Performance, processo di serializzazione, dimensioni del messaggio, tutto ne pagherebbe le conseguenze, inutilmente.

Il Data Transfer Object

Ancora una volta i pattern ci vengono in aiuto. Il Data Transfer Object (DTO, talvolta conosciuto anche come Value Object) è un oggetto che non appartiene al nostro dominio, contenente solo proprietà e nessun metodo o logica, ma soprattutto è un oggetto serializzabile ed ottimizzato per il semplice scopo per cui è stato creato: trasferimento di dati.
Nel nostro esempio avremmo dunque qualcosa di simile:

public class BookDTO
{
    public string Title { get; set; }
    public string AuthorName { get; set; }
    public double Price { get; set; }
}

Semplicemente quello che deve essere trasferito. Niente di più. Di conseguenza il contratto del nostro servizio risulterebbe quindi questo:

[ServiceContract]
public interface BookService
{
    [OperationContract]
    public BookDTO RetrieveBook(string isbn);
}
L‘implentazione del servizio ed il mapping dei dati

A questo punto il servizio si dovrebbe semplicemente occupare di invocare lo strato successivo, recuperare l’istanza dell’oggetto Book e poi restituirla al chiamante:

public BookDTO RetrieveBook(string isbn)
{
    Book book = OtherLayer.GetBook(isbn);
    BookDTO bookDTO = Map(book);
    return bookDTO;
}

Generalmente preferisco avere un servizio WCF che si occupa semplicemente di trasformare o adattare le esigenze di integrazione del mio dominio con il mondo esterno. Quindi pochissima logica, solo validazione dei dati e generazione di eventuali eccezioni (scordatevi l’accesso al database direttamente dal servizio Smile). Nel servizio quindi viene fatto il mapping del dominio verso l’entità esterna utilizzando logiche ad-hoc oppure appositi mapper. Il pattern DTO, come definito da Martin Fowler, prevere la realizzazione di un apposito componente chiamato assembler che “assembla” le informazioni e crea il DTO (o l’entità, a seconda della direzione). L’assembler deve essere sviluppato appositamente per ogni oggetto del dominio.

Preferisco, invece, l’utilizzo di un mapper che automaticamente mappa le entità a seconda delle esigenze. AutoMapper è decisamente un’ottimo punto di partenza. Consente infatti di eseguire il mapping di oggetti utilizzando specifiche regole. Nel nostro caso la funzione Map sarebbe semplicemente così composta:

private BookDTO Map(Book sourceBook)
{
    Mapper.CreateMap<Book, BookDTO>();
    return Mapper.Map<Book, BookDTO>(sourceBook);
}

AutoMapper effettuerà per noi la creazione di un istanza di BookDTO mappando le proprietà Title e Price da Book, e la proprietà AuthorName dalla proprietà Name della classe Author. Questo perchè nel momento in cui non trova nessuna proprietà con il nome della proprietà di destinazione e nessun metodo chiamato GetNomeProprietà, tenterà di suddividere il nome della proprietà di destinazione utilizzando il PascalCase per trovare il valore da assegnare (spero di essere stato chiaro Big Smile, altrimenti ditemelo…).

Qual’è il vantaggio? Non devo scrivere quintali di righe di codice per ogni mapping che devo eseguire. E non è mica poco se il mio dominio è decisamente complesso.

Conclusioni

Sono uno dei fan del DTO. Collegandomi al post precedente, la sequenza ottimale da seguire per la realizzazione di un buon strato di servizi è questa:

  • Definizione degli schema XSD
  • Definizione dei messaggi
  • WSDL
  • Generazione del codice. Che si suddivide poi in:
    • Creazione dei DTO (partendo dagli XSD)
    • Creazione dell’interfaccia del servizio (in parole povere l’interfaccia che marcheremo con ServiceContract e OperationContract)
    • Collegamento con il layer successivo, che comunica utilizzando gli oggetti del dominio e quindi generazione del mapping bidirezionale DTO <—> Domain Object

Vedo in questo approccio un’ottima suddivisione della logica del mio dominio con la logica di integrazione con i sistemi esterni. Un sistema esterno non è solo e necessariamente un’applicazione sviluppata da terze parti, ma anche una qualsiasi applicazione sempre da noi sviluppata e che fa parte della nostra soluzione, come ad esempio può essere un’applicazione Silverlight.

Apertissimo alla discussione Big Smile

Comments

Maurizio Tammacco said:

D'accordissimo con quanto affermi Fabio :-)

# October 12, 2009 2:39 AM

Ugo Lattanzi said:

Già, concordo.

Di fatto ne ho parlato qui

imperugo.tostring.it/.../dto

sia di Automapper, sia di un'implementazione custom con la Reflection.Emit buttando fuori IL che risulta essere più performante di automapper (rispetto alla versione che avevo provato io, magari ora le cose son cambiate).

Ciauz

# October 12, 2009 5:31 AM