Známé typy kontraktů dat
Třída KnownTypeAttribute umožňuje předem určit typy, které by měly být zahrnuty pro zvážení během deserializace. Pracovní příklad najdete v příkladu Známých typů .
Při předávání parametrů a návratových hodnot mezi klientem a službou obvykle oba koncové body sdílejí všechny datové kontrakty dat, které se mají přenášet. To však není případ v následujících případech:
Odeslaný datový kontrakt je odvozen od očekávaného datového kontraktu. Další informace najdete v části o dědičnosti v ekvivalenci kontraktů dat). V takovém případě přenášená data nemají stejný kontrakt dat, který očekává přijímající koncový bod.
Deklarovaný typ informací, které mají být přenášeny, je rozhraní, na rozdíl od třídy, struktury nebo výčtu. Proto nemůže být předem známo, který typ, který implementuje rozhraní, je skutečně odeslán, a proto přijímající koncový bod nemůže předem určit datový kontrakt pro přenášená data.
Deklarovaný typ informací, které mají být přenášeny, je Object. Vzhledem k tomu, že každý typ dědí z Objecta nemůže být předem známo, který typ je skutečně odeslán, přijímající koncový bod nemůže předem určit datový kontrakt pro přenášená data. Toto je zvláštní případ první položky: Každý kontrakt dat je odvozen od výchozího, prázdný datový kontrakt, který je generován pro Object.
Některé typy, které zahrnují typy rozhraní .NET Framework, mají členy, které jsou v jedné z předchozích tří kategorií. Hashtable Například používá Object k uložení skutečných objektů v tabulce hash. Při serializaci těchto typů nemůže přijímající strana určit předem kontrakt dat pro tyto členy.
PoleTypeAttribute – třída
Když data přicházejí do přijímajícího koncového bodu, modul runtime WCF se pokusí deserializovat data do instance typu CLR (Common Language Runtime). Typ, který se vytvoří instance pro deserializaci, je zvolen nejprve kontrolou příchozí zprávy a určit kontrakt dat, ke kterému odpovídá obsah zprávy. Modul deserializace se pak pokusí najít typ CLR, který implementuje kontrakt dat kompatibilní s obsahem zprávy. Sada kandidátských typů, které modul deserializace umožňuje během tohoto procesu, se označuje jako sada deserializátoru "známých typů".
Jedním ze způsobů, jak dát modulu deserializace vědět o typu je pomocí KnownTypeAttribute. Atribut nelze použít u jednotlivých datových členů, pouze u celých datových typů kontraktů. Atribut se použije u vnějšího typu , který může být třídou nebo strukturou. Ve svém nejzásadnějším použití určuje použití atributu typ jako "známý typ". To způsobí, že známý typ je součástí sady známých typů, kdykoli je objekt vnějšího typu nebo jakýkoli objekt odkazovaný prostřednictvím jeho členů deserializován. U stejného typu lze použít více než jeden KnownTypeAttribute atribut.
Známé typy a primitivy
Primitivní typy, stejně jako určité typy, které jsou považovány za primitivy (například DateTime a XmlElement) jsou vždy "známé" a nikdy není nutné přidávat prostřednictvím tohoto mechanismu. Pole primitivních typů však musí být přidána explicitně. Většina kolekcí se považuje za ekvivalentní polím. (Negenerické kolekce jsou považovány za ekvivalentní polím Object). Příklad použití primitiv, primitivních polí a primitivních kolekcí najdete v příkladu 4.
Poznámka:
Na rozdíl od jiných primitivních typů DateTimeOffset není struktura ve výchozím nastavení známým typem, takže je nutné ji ručně přidat do seznamu známých typů.
Příklady
Následující příklady ukazují třídu, která se KnownTypeAttribute používá.
Příklad 1
Existují tři třídy s vztahem dědičnosti.
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
<DataContract()> _
Public Class Shape
End Class
<DataContract(Name:="Circle")> _
Public Class CircleType
Inherits Shape
End Class
<DataContract(Name:="Triangle")> _
Public Class TriangleType
Inherits Shape
End Class
Následující CompanyLogo
třída může být serializována, ale nelze deserializovat, pokud ShapeOfLogo
je člen nastaven buď na CircleType
objekt nebo objekt TriangleType
, protože modul deserializace nerozpozná žádné typy s názvy kontraktů dat "Circle" nebo "Trojúhelník".
[DataContract]
public class CompanyLogo
{
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
<DataContract()> _
Public Class CompanyLogo
<DataMember()> _
Private ShapeOfLogo As Shape
<DataMember()> _
Private ColorOfLogo As Integer
End Class
Správný způsob zápisu CompanyLogo
typu se zobrazí v následujícím kódu.
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
<DataContract(), KnownType(GetType(CircleType)), KnownType(GetType(TriangleType))> _
Public Class CompanyLogo2
<DataMember()> _
Private ShapeOfLogo As Shape
<DataMember()> _
Private ColorOfLogo As Integer
End Class
Kdykoli je vnější typ CompanyLogo2
deserializován, modul deserializace zná CircleType
a TriangleType
, a proto je schopen najít odpovídající typy pro kontrakty dat "Circle" a "Trojúhelník".
Příklad 2
V následujícím příkladu, i když oba CustomerTypeA
a CustomerTypeB
mají Customer
kontrakt dat, instance CustomerTypeB
je vytvořena vždy, když PurchaseOrder
je deserializován, protože pouze CustomerTypeB
je známo deserializační modul.
public interface ICustomerInfo
{
string ReturnCustomerName();
}
[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
[DataMember]
ICustomerInfo buyer;
[DataMember]
int amount;
}
Public Interface ICustomerInfo
Function ReturnCustomerName() As String
End Interface
<DataContract(Name:="Customer")> _
Public Class CustomerTypeA
Implements ICustomerInfo
Public Function ReturnCustomerName() _
As String Implements ICustomerInfo.ReturnCustomerName
Return "no name"
End Function
End Class
<DataContract(Name:="Customer")> _
Public Class CustomerTypeB
Implements ICustomerInfo
Public Function ReturnCustomerName() _
As String Implements ICustomerInfo.ReturnCustomerName
Return "no name"
End Function
End Class
<DataContract(), KnownType(GetType(CustomerTypeB))> _
Public Class PurchaseOrder
<DataMember()> _
Private buyer As ICustomerInfo
<DataMember()> _
Private amount As Integer
End Class
Příklad 3
V následujícím příkladu Hashtable ukládá jeho obsah interně jako Object. Aby bylo možné úspěšně deserializovat tabulku hash, musí modul deserializace znát sadu možných typů, ke kterým může dojít. V tomto případě víme předem, že pouze Book
a Magazine
objekty jsou uloženy v objektu Catalog
, takže jsou přidány pomocí atributu KnownTypeAttribute .
[DataContract]
public class Book { }
[DataContract]
public class Magazine { }
[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
[DataMember]
System.Collections.Hashtable theCatalog;
}
<DataContract()> _
Public Class Book
End Class
<DataContract()> _
Public Class Magazine
End Class
<DataContract(), KnownType(GetType(Book)), KnownType(GetType(Magazine))> _
Public Class LibraryCatalog
<DataMember()> _
Private theCatalog As System.Collections.Hashtable
End Class
Příklad 4
V následujícím příkladu ukládá kontrakt dat číslo a operaci, která se má provést s číslem. Datovým Numbers
členem může být celé číslo, pole celých čísel nebo List<T> celé číslo obsahující celá čísla.
Upozornění
To bude fungovat pouze na straně klienta, pokud se SVCUTIL.EXE použije k vygenerování proxy serveru WCF. SVCUTIL.EXE načte metadata ze služby včetně všech známých typů. Bez těchto informací klient nebude moct deserializovat typy.
[DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
private object numberValue;
[DataMember]
public object Numbers
{
get { return numberValue; }
set { numberValue = value; }
}
//[DataMember]
//public Operation Operation;
}
<DataContract(), KnownType(GetType(Integer()))> _
Public Class MathOperationData
Private numberValue As Object
<DataMember()> _
Public Property Numbers() As Object
Get
Return numberValue
End Get
Set(ByVal value As Object)
numberValue = value
End Set
End Property
End Class
Toto je kód aplikace.
// This is in the service application code:
static void Run()
{
MathOperationData md = new MathOperationData();
// This will serialize and deserialize successfully because primitive
// types like int are always known.
int a = 100;
md.Numbers = a;
// This will serialize and deserialize successfully because the array of
// integers was added to known types.
int[] b = new int[100];
md.Numbers = b;
// This will serialize and deserialize successfully because the generic
// List<int> is equivalent to int[], which was added to known types.
List<int> c = new List<int>();
md.Numbers = c;
// This will serialize but will not deserialize successfully because
// ArrayList is a non-generic collection, which is equivalent to
// an array of type object. To make it succeed, object[]
// must be added to the known types.
ArrayList d = new ArrayList();
md.Numbers = d;
}
' This is in the service application code:
Shared Sub Run()
Dim md As New MathOperationData()
' This will serialize and deserialize successfully because primitive
' types like int are always known.
Dim a As Integer = 100
md.Numbers = a
' This will serialize and deserialize successfully because the array of
' integers was added to known types.
Dim b(99) As Integer
md.Numbers = b
' This will serialize and deserialize successfully because the generic
' List(Of Integer) is equivalent to Integer(), which was added to known types.
Dim c As List(Of Integer) = New List(Of Integer)()
md.Numbers = c
' This will serialize but will not deserialize successfully because
' ArrayList is a non-generic collection, which is equivalent to
' an array of type object. To make it succeed, object[]
' must be added to the known types.
Dim d As New ArrayList()
md.Numbers = d
End Sub
Známé typy, dědičnost a rozhraní
Pokud je známý typ přidružen k určitému typu pomocí KnownTypeAttribute
atributu, známý typ je také přidružen ke všem odvozeným typům tohoto typu. Podívejte se například na následující kód.
[DataContract]
[KnownType(typeof(Square))]
[KnownType(typeof(Circle))]
public class MyDrawing
{
[DataMember]
private object Shape;
[DataMember]
private int Color;
}
[DataContract]
public class DoubleDrawing : MyDrawing
{
[DataMember]
private object additionalShape;
}
<DataContract(), KnownType(GetType(Square)), KnownType(GetType(Circle))> _
Public Class MyDrawing
<DataMember()> _
Private Shape As Object
<DataMember()> _
Private Color As Integer
End Class
<DataContract()> _
Public Class DoubleDrawing
Inherits MyDrawing
<DataMember()> _
Private additionalShape As Object
End Class
Třída DoubleDrawing
nevyžaduje použití atributu KnownTypeAttribute
AdditionalShape
Square
a Circle
v poli, protože základní třída (Drawing
) již tyto atributy používá.
Známé typy mohou být přidruženy pouze ke třídám a strukturám, nikoli rozhraním.
Známé typy používající otevřené obecné metody
Může být nutné přidat obecný typ jako známý typ. Otevřený obecný typ však nelze předat jako parametr atributu KnownTypeAttribute
.
Tento problém lze vyřešit pomocí alternativního mechanismu: Napište metodu, která vrátí seznam typů, které se mají přidat do kolekce známých typů. Název metody se pak zadává jako řetězcový argument KnownTypeAttribute
atributu kvůli určitým omezením.
Metoda musí existovat u typu, ke kterému KnownTypeAttribute
je atribut použit, musí být statický, nesmí přijmout žádné parametry a musí vrátit objekt, ke IEnumerableTypekterému lze přiřadit .
Atribut nelze kombinovat KnownTypeAttribute
s názvem metody a KnownTypeAttribute
atributy se skutečnými typy stejného typu. Kromě toho nelze použít více než jeden KnownTypeAttribute
s názvem metody pro stejný typ.
Podívejte se na následující třídu.
[DataContract]
public class DrawingRecord<T>
{
[DataMember]
private T theData;
[DataMember]
private GenericDrawing<T> theDrawing;
}
<DataContract()> _
Public Class DrawingRecord(Of T)
<DataMember()> _
Private theData As T
<DataMember()> _
Private theDrawing As GenericDrawing(Of T)
End Class
Pole theDrawing
obsahuje instance obecné třídy ColorDrawing
a obecné třídy BlackAndWhiteDrawing
, z nichž obě dědí z obecné třídy Drawing
. Za normálních okolností musí být oba přidány ke známým typům, ale následující syntaxe pro atributy není platná.
// 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)))>
Proto musí být vytvořena metoda pro vrácení těchto typů. Správný způsob, jak tento typ napsat, pak se zobrazí v následujícím kódu.
[DataContract]
[KnownType("GetKnownType")]
public class DrawingRecord2<T>
{
[DataMember]
private T TheData;
[DataMember]
private GenericDrawing<T> TheDrawing;
private static Type[] GetKnownType()
{
Type[] t = new Type[2];
t[0] = typeof(ColorDrawing<T>);
t[1] = typeof(BlackAndWhiteDrawing<T>);
return t;
}
}
<DataContract(), KnownType("GetKnownType")> _
Public Class DrawingRecord2(Of T)
Private TheData As T
Private TheDrawing As GenericDrawing(Of T)
Private Shared Function GetKnownType() As Type()
Dim t(1) As Type
t(0) = GetType(ColorDrawing(Of T))
t(1) = GetType(BlackAndWhiteDrawing(Of T))
Return t
End Function
End Class
Další způsoby přidání známých typů
Kromě toho lze známé typy přidat prostřednictvím konfiguračního souboru. To je užitečné, když neřídíte typ, který vyžaduje známé typy pro správnou deserializaci, například při použití knihoven typů třetích stran se službou Windows Communication Foundation (WCF).
Následující konfigurační soubor ukazuje, jak zadat známý typ v konfiguračním souboru.
<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>
V předchozím konfiguračním souboru je volána MyCompany.Library.Shape
datový typ kontraktu je deklarován jako MyCompany.Library.Circle
známý typ.