2013-09-16 10 views
26

Stiamo creando un'app Web utilizzando AngularJS, C#, API Web ASP.Net e Fluent NHibernate. Abbiamo deciso di utilizzare i DTO per trasferire i dati al livello di presentazione (viste angolari). Ho avuto qualche dubbio riguardo alla strutturazione e alla denominazione generale dei DTO. Ecco un esempio per illustrare il mio scenario. Diciamo che ho un'entità dominio chiamato cliente che si presenta come:Convenzioni di denominazione DTO, modellazione ed ereditarietà

public class Customer 
    { 
     public virtual int Id { get; set; } 
     public virtual string Name { get; set; } 
     public virtual Address Address { get; set; } 
     public virtual ICollection<Account> Accounts { get; set; } 
    } 

Ora, nel mio punto di vista/strato di presentazione ho bisogno di recuperare i diversi gusti di clienti come:

1) Basta Id e nome 2) Id, nome e indirizzo 3) Id, Nome, Indirizzo e Bilancio

ho creato una serie di DTOs per raggiungere questo obiettivo:

public class CustomerEntry 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class CustomerWithAddress : CustomerEntry 
{ 
    public AddressDetails Address { get; set; } 
} 

public class CustomerWithAddressAndAccounts : CustomerWithAddress 
{ 
    public ICollection<AccountDetails> Accounts { get; set; } 
} 

AddressDetails e AccountDetails sono DTO che hanno tutte le proprietà delle rispettive entità di dominio corrispondenti.

Questo funziona perfettamente per query e dati recuperati; la domanda è cosa devo usare per inserti e aggiornamenti. Durante la creazione di un nuovo record del cliente, il nome e l'indirizzo sono obbligatori e gli account sono facoltativi. In altre parole, ho bisogno di un oggetto con tutte le proprietà del cliente. Da qui la confusione:

1) Cosa uso per inserire e aggiornare? Il DTO CustomerWithAddressAndAccounts ha tutto in esso ma il suo nome sembra un po 'scomodo da utilizzare per inserimento/aggiornamenti.

2) Creo un altro DTO .. ​​se lo faccio, non sarebbe una duplicazione dato che il nuovo DTO sarà esattamente come CustomerWithAddressAndAccounts?

3) Ultimo ma non meno importante, l'eredità di DTO strcuture descritta sopra sembra una buona misura per il requisito? Ci sono altri modi per modellarlo?

Ho passato altri post su questo argomento ma non ho potuto fare molti progressi. Una cosa che ho scelto è stata evitare di usare il suffisso "DTO" nei nomi delle classi. Penso che sia un po 'superfluo.

piacerebbe sentire i vostri pensieri

Grazie

risposta

9

raccomandazione è che si dovrebbe solo avere una classe DTO per ogni entità suffisso DTO esempio CustomerEntryDTO per Customerentity (ma è possibile utilizzare le gerarchie di ereditarietà per scelta e requisiti).

Inoltre, aggiungere un abstract DTOBase tipo di classe base o un'interfaccia; e non utilizzare gerarchie di ereditarietà così profonde per ciascun indirizzo, account e altre proprietà da includere nei DTO figli. Piuttosto, includere queste proprietà nella stessa CustomerEntryDTO classe (se possibile), come di seguito:

[Serializable] 
public class CustomerEntryDTO : DTOBase, IAddressDetails, IAccountDetails 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public AddressDetails Address { get; set; } //Can remain null for some Customers 
    public ICollection<AccountDetails> Accounts { get; set; } //Can remain null for some Customemer 
} 

Inoltre, i tuoi DTOs dovrebbe essere serializable per essere passato attraverso i limiti del processo.

Per più sul modello DTO, vedere qui di seguito gli articoli:

Data Transfer Object

MSDN

Edit: Nel caso in cui non si desidera inviare alcune proprietà oltre il filo (So ​​che avresti bisogno di questo in modo condizionale, quindi avrei bisogno di esplorare di più su questo), puoi escluderli dal meccanismo di serializzazione usando attributi come NonSerialized (ma funziona solo sui campi e non sulle proprietà, vedere l'articolo sulla soluzione alternativa per l'utilizzo con le proprietà: NonSerialized on property). È inoltre possibile creare il proprio attributo personalizzato come ExcludeFromSerializationAttribute e applicarlo alle proprietà che non si desidera inviare ogni volta su un filo in base a determinate regole/condizioni. Vedi anche:Conditional xml serialization

Edit 2: utilizzare interfacce per separare le diverse proprietà in quello CustomerEntryDTO di classe. Vedi il principio di segregazione dell'interfaccia su Google o MSDN. Proverò a mettere una spiegazione esemplificativa più avanti.

+0

Grazie per la risposta. Una delle motivazioni per l'utilizzo di più classi DTO doveva essere un po 'più espressiva nel definire a cosa serve il DTO. Inoltre, se hai solo bisogno di Id e Nome, dovresti davvero inviare l'intero CustomerEntryDTO (che hai descritto) sul filo? – Sennin

+0

@Sennin Ho modificato la mia risposta in base al tuo commento ma penso che la mia risposta sarebbe ancora incompleta per te. Forse lo modificherò in seguito per aggiungere altro sul tuo commento sopra. – VS1

+0

@Sennin pls vedere la mia modifica 2. – VS1

0

A partire dall'articolo 1, per inserti e aggiornamenti è preferibile utilizzare lo schema di comando. Secondo CQRS, non hai bisogno di DTO. Considerate questo schema: CQRS — basic patterns via blogs.msdn.com

+0

Hai ancora bisogno di un DTO per il lato query delle cose, quindi non una buona risposta. –

0

Cosa posso usare per l'inserimento e gli aggiornamenti?

  1. operazioni di servizio sono solitamente definiti in stretta relazione alle operazioni di business. La lingua commerciale non parla in termini di "inserti" e "aggiornamenti", né di servizi.

  2. È probabile che il servizio di gestione clienti abbia un'operazione Register che richiede il nome del cliente e forse alcuni altri parametri facoltativi.

creo un'altra DTO?

Sì, è necessario creare un altro DTO.

volte il servizio contratto operazione può essere sufficiente e non v'è alcuna necessità di definire un DTO separato per una particolare operazione:

function Register(UserName as String, Address as Maybe(of String)) as Response 

Ma la maggior parte del tempo è meglio definire una classe DTO separata anche per solo una singola operazione di servizio:

class RegisterCommand 
    public UserName as String 
    public Address as Maybe(of String) 
end class 

function Register(Command as RegisterCommand) as Response 

RegisterCommand DTO può sembrare molto simile a CustomerWithAddress DTO perché ha gli stessi campi ma in realtà queste 2 DTOs hanno significati molto diversi e non sostituirsi.

Ad esempio, CustomerWithAddress contiene AddressDetails, mentre una semplice rappresentazione di indirizzo String può essere sufficiente per registrare un cliente.

L'utilizzo di un DTO separato per ciascuna operazione di manutenzione richiede più tempo per scrivere ma è più semplice da gestire.