Bekannte Typen in Datenverträgen
Die KnownTypeAttribute-Klasse ermöglicht es Ihnen, vorab die Typen anzugeben, die während der Deserialisierung in Betracht gezogen werden sollen. Ein Arbeitsbeispiel finden Sie unter Known Types.
Beim Übergeben von Parametern und Rückgabewerten zwischen einem Client und einem Dienst verwenden normalerweise beide Endpunkte sämtliche Datenverträge für die zu übertragenden Daten gemeinsam. Unter den folgenden Umständen ist dies allerdings nicht der Fall:
- Der gesendete Datenvertrag wird vom erwarteten Datenvertrag abgeleitet. Weitere Informationen finden Sie unter dem Abschnitt zur Vererbung unter Datenvertragsäquivalenz. In diesem Fall gilt für die übertragenen Daten nicht der Datenvertrag, der vom empfangenden Endpunkt erwartet wird.
- Die zu übertragenden Informationen werden als zu einem Schnittstellentyp zugehörig deklariert, nicht als Klasse, Struktur oder Enumeration. Daher kann nicht im Vorhinein bekannt sein, welcher die Schnittstelle implementierende Typ tatsächlich gesendet wird, und folglich kann der empfangende Endpunkt nicht vorab den Datenvertrag für die übermittelten Daten bestimmen.
- Für die zu sendenden Informationen wird der Typ Object deklariert. Weil jeder Typ von Object erbt und nicht im Vorhinein bekannt sein kann, welcher die Schnittstelle implementierender Typ tatsächlich gesendet wird, und kann der empfangende Endpunkt nicht vorab den Datenvertrag für die übermittelten Daten bestimmen. Dies ist ein Sonderfall des ersten Punkts: Jeder Datenvertrag ist vom Standardvertrag abgeleitet, d. h. einem leeren Vertrag, der für Object generiert wird.
- Einige Typen, einschließlich der .NET Framework-Typen, verfügen über Member, die einer der drei oben genannten Kategorien angehören. Zum Beispiel verwendet Hashtable den Typ Object, um die tatsächlichen Objekte in der Hashtabelle zu speichern. Beim Serialisieren dieser Typen kann die Empfängerseite nicht im Voraus den Datenvertrag für diese Member bestimmen.
Die KnownTypeAttribute-Klasse
Wenn die Daten beim empfangenden Endpunkt ankommen, versucht das WCF-Laufzeitmodul, die Daten in eine Instanz eines CLR (Common Language Runtime)-Typs zu deserialisieren. Zur Auswahl des Typs, der für die Deserialisierung instanziiert wird, wird zuerst die eingehende Nachricht überprüft, um den Datenvertrag zu ermitteln, dem der Inhalt der Nachricht entspricht. Das Deserialisierungsmodul versucht dann, den CLR-Typ zu finden, der einen mit dem Nachrichteninhalt kompatiblen Datenvertrag implementiert. Die Gruppe potenzieller Typen, die das Deserialisierungsmodul während dieses Prozesses zulässt, wird als die Gruppe "bekannter Typen" des Deserialisierers bezeichnet.
Eine Methode, das Deserialisierungsmodul über einen Typ zu informieren, besteht im Einsatz von KnownTypeAttribute. Das Attribut kann nicht auf einzelne Datenmember, sondern nur auf gesamte Datenvertragstypen angewendet werden. Das Attribut wird auf einen äußeren Typ angewendet, der eine Klasse oder eine Struktur sein kann. Grundsätzlich wird durch die Anwendung des Attributs ein Typ als "bekannter Typ" angegeben. Dadurch wird der bekannte Typ Bestandteil der Gruppe bekannter Typen, wenn ein Objekt des äußeren Typs oder ein Objekt, auf das durch ein Member des äußeren Typs verwiesen wird, deserialisiert wird. Auf einen Typ können mehrere KnownTypeAttribute-Attribute angewendet werden.
Bekannte Typen und Primitive
Primitive Typen sowie bestimmte Typen, die als primitiv behandelt werden (z. B. DateTime und XmlElement) sind immer "bekannt" und müssen nie mithilfe dieses Mechanismus hinzugefügt werden. Arrays von primitiven Typen müssen allerdings explizit hinzugefügt werden. Die meisten Auflistungen werden als äquivalent mit Arrays betrachtet. (Nicht generische Auflistungen werden als äquivalent mit Arrays von Object betrachtet). Ein Beispiel für die Verwendung von Primitiven, primitiven Arrays und primitiven Auflistungen finden Sie in Beispiel 4.
Tipp
Im Gegensatz zu anderen primitiven Typen ist die DateTimeOffset-Struktur standardmäßig kein bekannter Typ. Daher muss sie der Liste bekannter Typen manuell hinzugefügt werden.
Beispiele
In den folgenden Beispielen wird die Verwendung der KnownTypeAttribute-Klasse veranschaulicht.
Beispiel 1
Es sind drei Klassen mit einer Vererbungsbeziehung gegeben.
Die folgende CompanyLogo
-Klasse kann serialisiert werden. Sie kann jedoch nicht deserialisiert werden, wenn der ShapeOfLogo
-Member auf ein CircleType
-Objekt oder ein TriangleType
-Objekt festgelegt wird, weil das Deserialisierungsmodul keine Typen mit Datenverträgen namens "Circle" oder "Triangle" erkennt.
Im folgenden Code wird gezeigt, wie der CompanyLogo
-Typ richtig geschrieben wird.
Jedes Mal, wenn der äußere Typ CompanyLogo2
deserialisiert wird, wird das Deserialisierungsmodul über CircleType
und TriangleType
informiert und kann daher die passenden Typen für die Datenverträge namens "Circle" und "Triangle" finden.
Beispiel 2
Im folgenden Beispiel ist sowohl CustomerTypeA
als auch CustomerTypeB
der Datenvertrag Customer
zugeordnet. Trotz dieser Tatsache wird jedes Mal eine Instanz von CustomerTypeB
erstellt, wenn ein PurchaseOrder
-Objekt deserialisiert wird, weil das Deserialisierungsmodul nur CustomerTypeB
kennt.
Beispiel 3
Im folgenden Beispiel speichert ein Hashtable seinen Inhalt intern als Object. Um eine Hashtabelle erfolgreich deserialisieren zu können, muss das Deserialisierungsmodul die Gruppe möglicher Typen kennen, die hier vorkommen können. In diesem Fall wissen wir im Vorhinein, dass nur Book
-Objekte und Magazine
-Objekte im Catalog
gespeichert werden. Daher werden sie mithilfe des KnownTypeAttribute-Attributs hinzugefügt.
Beispiel 4
Im folgenden Beispiel wird eine Zahl und ein Vorgang, der mit der Zahl ausgeführt werden soll, in einem Datenvertrag gespeichert. Der Numbers
-Datenmember kann eine ganze Zahl, ein Array von ganzen Zahlen oder ein Objekt des Typs List sein, das ganze Zahlen enthält.
Dies ist der Anwendungscode.
Bekannte Typen, Vererbung und Schnittstellen
Wenn ein bekannter Typ mithilfe des KnownTypeAttribute-Attributs mit einem bestimmten Typ verknüpft wird, wird der bekannte Typ auch mit allen von diesem Typ abgeleiteten Typen verknüpft. Beachten Sie beispielsweise folgenden Code.
Die DoubleDrawing
-Klasse erfordert nicht, dass das KnownTypeAttribute-Attribut mit Square
und Circle
im AdditionalShape
-Feld verwendet wird, weil diese Attribute bereits auf die Basisklasse (Drawing
) angewendet wurden.
Bekannte Typen können nur Klassen und Strukturen, nicht jedoch Schnittstellen, zugeordnet werden.
Bekannte Typen, die offene generische Methoden verwenden
Es ist möglicherweise notwendig, einen generischen Typ als bekannten Typ hinzuzufügen. Ein offener generischer Typ kann dem KnownTypeAttribute-Attribut nicht als Parameter übergeben werden.
Dieses Problem lässt sich mithilfe eines alternativen Mechanismus lösen: Schreiben Sie eine Methode, die eine Liste derjenigen Typen zurückgibt, welche der Auflistung bekannter Typen hinzugefügt werden können. Wegen einigen Beschränkungen wird der Name der Methode dann als Zeichenfolgenargument für das KnownTypeAttribute-Attribut angegeben.
Diese Methode muss für den Typ vorhanden sein, auf den das KnownTypeAttribute-Attribut angewendet wird. Sie muss statisch sein, darf keine Parameter annehmen und muss ein Objekt zurückgeben, das der IEnumerable-Schnittstelle von Type zugewiesen werden kann.
Es ist nicht möglich, das KnownTypeAttribute-Attribut mit einem Methodennamen und KnownTypeAttribute-Attribute mit tatsächlichen Typen für den gleichen Typ zu kombinieren. Weiterhin ist es nicht möglich, mehr als ein KnownTypeAttribute-Attribut mit einem Methodennamen auf den gleichen Typ anzuwenden.
Siehe folgende Klasse.
Das theDrawing
-Feld enthält Instanzen der generischen Klasse ColorDrawing
und der generischen Klasse BlackAndWhiteDrawing
, die beide von der generischen Klasse Drawing
abgeleitet sind. Normalerweise müssen beide Klassen den bekannten Typen hinzugefügt werden, aber der folgende Code stellt keine für Attribute gültige Syntax dar.
// Invalid syntax for attributes:
// [KnownType(typeof(ColorDrawing<T>))]
// [KnownType(typeof(BlackAndWhiteDrawing<T>))]
' Invalid syntax for attributes:
' <KnownType(GetType(ColorDrawing(Of T))), _
' KnownType(GetType(BlackAndWhiteDrawing(Of T)))>
Daher muss eine Methode erstellt werden, die diese Typen zurückgibt. Im folgenden Code wird gezeigt, wie dieser Typ richtig geschrieben wird.
Weitere Verfahren zum Hinzufügen bekannter Typen
Sie können Typen zudem der ReadOnlyCollection-Auflistung hinzufügen, auf die über die KnownTypes-Eigenschaft des DataContractSerializer-Objekts zugegriffen wird.
Darüber hinaus können bekannte Typen durch eine Konfigurationsdatei hinzugefügt werden. Dies ist hilfreich, wenn Sie keinen Einfluss auf den Typ haben, dessen Deserialisierung bekannte Typen erfordert, beispielsweise bei Verwendung von Typbibliotheken von Drittanbietern in Windows Communication Foundation (WCF).
Die folgende Konfigurationsdatei zeigt, wie in einer Konfigurationsdatei ein bekannter Typ angegeben wird.
<configuration>
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyCompany.Library.Shape,
MyAssembly, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
<knownType type="MyCompany.Library.Circle,
MyAssembly, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
</configuration>
In der vorangegangenen Konfigurationsdatei wird ein Datenvertragstyp mit der Bezeichnung MyCompany.Library.Shape
deklariert, um MyCompany.Library.Circle
als bekannten Typ zu erhalten.
Siehe auch
Referenz
KnownTypeAttribute
Hashtable
Object
DataContractSerializer
KnownTypes