Datenvertrag
Dieses Dokument beschreibt das Muster Datenvertrag in Hinblick auf WCF und WebService basierte Schnittstellen. Nach diesem Muster können Typen entwickelt werden, die zum Datenaustausch zwischen lose gekoppelten Einheiten (insbesondere unterschiedlichen Produkten) genutzt werden. Insbesondere zum Datenaustausch mit der Applikationslogik können nach diesem Muster aufgebaute Datenverträge genutzt werden. Das Muster wird als Klasse implementiert.
Design
In Schnittstellen, die Daten zwischen lose gekoppelten Einheiten (z.B. Workflow-Instanzen und Applikationslogik) austauschen, sollen nur Standard-Datentypen (s. Datentypen und Datentyp-Mapping) verwendet werden. Der Datenvertrag ist Bestandteil der Schnittstelle und wird daher der Interface-Assembly hinzugefügt. Als Namensraum ist Contracts zu verwenden (1.0: Deklaration von Namensräumen (Namespace)). Als Postfix der Klasse ist zur Unterscheidung vom Domänenmodell das Postfix Contract zu verwenden.
Aus Schnittstellensicht kann man sich einen Datenvertrag als XML-Dokument vorstellen.
Die Datenverträge haben nichts mit Domänen-Modellen zu tun! Ein Datenvertrag stellt die serviceorientierte Umsetzung eins DTOs (s. http://martinfowler.com/eaaCatalog/dataTransferObject.html) dar.
Die Nutzung eines Datenvertrags ist auf die Schnittstellen der definierenden Applikation beschränkt. Dies bedeutet, dass eine Serverschnittstelle keine Datenverträge einer anderen CS Applikation entgegen nehmen darf. Auch dürfen in Datenverträgen keine Referenzen auf Datenverträge bzw. Enumerationen anderer Applikationen vorkommen.
Sollen die Datenverträge zur späteren Verwendung in der Datenbank abgelegt werden, wird die XML-Serialisierung und -Deserialisierung über die Klasse XmlStringSerializer
genutzt (vgl. Muster - Serialisierung von Datenverträgen (DataContractStringSerializer), 1.0). Zur Kommunikation mit durch Kunden anpassbaren Komponenten (z.B. Workflow) kann ein XML-Schema über die XML Datentypen veröffentlicht werden.
Implementierung
Ein Datenvertrag wird in Form einer Klasse implementiert. Die Klasse wird mit dem Attribut DataContract
versehen.
Das Attribut DataContract
wird durch das .NET Framework zur Verfügung gestellt. Die Assembly System.Runtime.Serialization muss eingebunden sein.
using System.Runtime.Serialization; [Pattern(Schleupen.CS.SY.Patterns.DataContract.Container41)] [DataContract(Namespace = ALXmlProvider.TasksNamespaceUri, IsReference = true)] [Serializable] public sealed class TaskContract { // ... }
Der Namespace sollte über eine separate Klasse zurückgegeben werden. Diese kann analog zur Klasse Schleupen.CS.SY.WF.ALXmlProvider
aufgebaut werden (vgl. Muster - Deklaration von Namensräumen (Namespace), 1.0):
public static class ALXmlProvider { public const string NamespaceUri = "urn:CS.SY"; public const string TasksNamespaceUri = NamespaceUri + "/Tasks"; // ... }
Die Eigenschaft IsReference
sollte stets true
sein. Dies führt dazu, dass die Objekte in der serialisierten Darstellung mit einer eine ID-Eigenschaft versehen werden. Wird ein serialisierte Objekt in einem anderen Kontext erneut verwendet wird auf diese ID verwiesen, statt den Serialisierungsprozess erneut durchzuführen. Diese Eigenschaft verringert die Nachrichtengröße, erzeugt auf der Deserialisierungsseite den gleichen Objektgraphen (Referenzgleichheit) und unterstützt zyklische Referenzen die ansonsten zu einer Serialisierungsausnahme führen.
Elemente und Eigenschaften
Jede zum Datenaustausch relevante Eigenschaft muss mit dem Attribut DataMember
versehen werden. Nicht explizit mit DataMember
angegebene Eigenschaften werden beim Serialisieren ignoriert.
[DataMember] public string TypeShortcut { get; set; }
Um das Datenvolumen des Contracts gering zu halten sollten Werte nur serialisiert werden, wenn sie ungleich dem Standardwert sind. Dazu muss beim DataMember
-Attribut EmitDefaultValue
auf false
gesetzt werden.
[DataMember(EmitDefaultValue=false)] public int? Id{ get; set; } [DataMember(EmitDefaultValue=false)] public int Count{ get; set; }
Die Zuweisung eines Standardwertes ist durch folgenden Code zu realisieren. Optional kann die Eigenschaften des Datenvertrages mit dem Attribut DefaultValue
versehen werden um die Unterstützung mit dem Designer zu gewährleisten.