2013-09-16 3 views
26

Wir bauen ein Web-App AngularJS, C#, ASP.Net Web-API und Fluent NHibernate. Wir haben entschieden, DTOs zu verwenden, um Daten in die Präsentationsschicht (Winkelansichten) zu übertragen. Ich hatte einige Zweifel bezüglich der allgemeinen Strukturierung und Benennung von DTOs. Hier ist ein Beispiel, um mein Szenario zu veranschaulichen. Lets sagen, dass ich einen Domain-Entität namens Customer haben, die wie folgt aussieht: JetztDTO Namenskonventionen, Modellierung und Vererbung

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

in meine Ansichten/Präsentationsschicht ich verschiedene Varianten von Kunden wie abrufen müssen:

1) Nur Identifikation und Name 2) ID, Name und Anschrift 3) Id, Name, Adresse und Konten

ich eine Reihe von DTOs erstellt haben, um dies zu erreichen:

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 und AccountDetails sind DTOs, die alle Eigenschaften ihrer entsprechenden Domain-Entitäten haben.

Diese für die Abfrage und Datenabfragen gut funktioniert; Die Frage ist, was verwende ich für Einsätze und Updates. Bei der Erstellung eines neuen Kundendatensatzes sind Name und Adresse obligatorisch und Konten sind optional. Also brauche ich ein Objekt mit allen Kundeneigenschaften. Daraus ergibt sich die Verwirrung:

1) Was kann ich für Einsatz und Updates? Das CustomerWithAddressAndAccounts-DTO enthält alles, aber der Name scheint ein wenig umständlich zu sein, um für das Einfügen/Aktualisieren verwendet zu werden.

2) erstelle ich einen anderen DTO .. ​​wenn ich tun würde, dass keine Duplizierung als die neue DTO wird genau wie CustomerWithAddressAndAccounts sein?

3) Last but not least, ist der DTO Erbe strcuture oben beschrieben scheint wie eine gute Passform für die Anforderung? Gibt es andere Möglichkeiten, dies zu modellieren?

Ich habe durch andere Beiträge zu diesem Thema gegangen, kann aber nicht viel Fortschritte machen. Eine Sache, die ich abhole, war, das Suffix "DTO" in den Klassennamen zu vermeiden. Ich denke, es ist ein bisschen überflüssig.

Würde gerne Ihre Gedanken

Dank hören

Antwort

9

Empfehlung ist, dass Sie nur für jede Entität suffixed mit DTO ein DTO-Klasse zum Beispiel haben sollte CustomerEntryDTO für die Customerentity (aber Sie können sicherlich Vererbungshierarchien nach Wahl und Anforderungen verwenden).

Darüber hinaus fügen Sie eine abstrakte DTOBase Art der Basisklasse oder eine Schnittstelle; und verwenden Sie keine solchen Vererbungsherarchien für jede Adresse, jedes Konto und andere Eigenschaften, die in untergeordneten DTOs enthalten sein sollen. Vielmehr sind diese Eigenschaften in der gleichen CustomerEntryDTO Klasse (wenn möglich), wie unten:

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

Darüber hinaus Ihre DTOs sollte sein serializable über Prozessgrenzen hinweg übergeben werden.

Für mehr auf dem DTO-Muster, unter Artikel verweisen:

Data Transfer Object

MSDN

Edit: Falls Sie nicht wollen, bestimmte Eigenschaften über den Draht senden (Ich weiß, dass Sie dies bedingt benötigen würden, also müssten Sie mehr darüber herausfinden), Sie können sie vom Serialisierungsmechanismus ausschließen, indem Sie Attribute wieverwenden(funktioniert aber nur für Felder und nicht für Eigenschaften, siehe Problemumgehungsartikel für die Verwendung mit Eigenschaften: NonSerialized on property). Sie können auch Ihr eigenes benutzerdefiniertes Attribut wie ExcludeFromSerializationAttribute erstellen und es auf Eigenschaften anwenden, die nicht jedes Mal aufgrund bestimmter Regeln/Bedingungen über Kabel gesendet werden sollen. siehe auch:Conditional xml serialization

Edit 2: Verwenden Schnittstellen zur Trennung der verschiedenen Objekte in einer CustomerEntryDTO Klasse. Sehen Sie sich das Interface-Trennungsprinzip bei Google oder MSDN an. Ich werde später versuchen, eine Beispielerklärung zu geben.

+0

Vielen Dank für Ihre Antwort. Eine der Motivationen für die Verwendung mehrerer DTO-Klassen bestand darin, etwas expressiver zu sein, um zu definieren, wofür das DTO verwendet wird. Wenn Sie nur die ID und den Namen benötigen, sollten Sie wirklich das vollwertige CustomerEntryDTO (das Sie beschrieben haben) über die Leitung senden? – Sennin

+0

@Sennin Ich habe meine Antwort basierend auf deinem Kommentar bearbeitet, aber ich denke meine Antwort wäre für dich noch unvollständig. Möglicherweise werde ich es später bearbeiten, um mehr zu Ihrem obigen Kommentar hinzuzufügen. – VS1

+0

@Sennin pls siehe meine Bearbeitung 2. – VS1

0

Ab Ihrem Artikel 1 ist es besser, für Einfügungen und Aktualisierungen das Befehlsmuster zu verwenden. Laut CQRS benötigen Sie keine DTOs. Betrachten Sie dieses Schema: CQRS — basic patterns über blogs.msdn.com

+0

Sie brauchen immer noch ein DTO für die Abfrageseite der Dinge, also keine gute Antwort. –

0

Was kann ich für Insert und Updates?

  1. Servicearbeiten sind in der Regel in sehr engen Zusammenhang mit Geschäftstätigkeit definiert. Business-Sprache spricht nicht in Bezug auf "Einfügungen" und "Updates", noch tun Dienstleistungen.

  2. Kunden-Management-Service wird wahrscheinlich einige Register Operation, die den Namen des Kunden und vielleicht einige andere optionale Parameter hat.

Muss ich einen anderen DTO schaffen?

Ja, Sie sollten ein anderes DTO erstellen.

Manchmal Servicebetrieb Vertrag kann genug sein, und es besteht keine Notwendigkeit, eine separate DTO für eine bestimmte Operation zu definieren:

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

Aber die meiste Zeit ist es besser, eine separate DTO-Klasse zu definieren, auch nur für ein einzelner Service-Betrieb:

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

function Register(Command as RegisterCommand) as Response 

RegisterCommand DTO kann CustomerWithAddress DTO sehr ähnlich aussehen, weil es die gleichen Felder hat, aber diese 2 DTOs haben sehr unterschiedliche Bedeutungen in der Tat und nicht als Ersatz für sie.

Zum Beispiel enthält CustomerWithAddress enthält AddressDetails, während eine einfache String Adresse Darstellung kann ausreichen, um einen Kunden zu registrieren.

Die Verwendung eines separaten DTO für jeden Servicevorgang dauert länger, ist aber einfacher zu verwalten.