Delen via


Bekende typen gegevenscontract

Met KnownTypeAttribute de klasse kunt u vooraf de typen opgeven die ter overweging moeten worden opgenomen tijdens de deserialisatie. Zie het voorbeeld Bekende typen voor een werkvoorbeeld.

Normaal gesproken delen beide eindpunten bij het doorgeven van parameters en retourwaarden tussen een client en een service alle gegevenscontracten van de gegevens die moeten worden verzonden. Dit is echter niet het geval in de volgende omstandigheden:

  • Het verzonden gegevenscontract wordt afgeleid van het verwachte gegevenscontract. Zie de sectie over overname in gelijkwaardigheid van gegevenscontract voor meer informatie. In dat geval hebben de verzonden gegevens niet hetzelfde gegevenscontract als verwacht door het ontvangende eindpunt.

  • Het gedeclareerde type voor de informatie die moet worden verzonden, is een interface, in plaats van een klasse, structuur of opsomming. Daarom kan het niet vooraf bekend zijn welk type de interface implementeert, en daarom kan het ontvangende eindpunt het gegevenscontract voor de verzonden gegevens niet vooraf bepalen.

  • Het gedeclareerde type voor de informatie die moet worden verzonden, is Object. Omdat elk type overdraagt van Object, en het niet vooraf bekend is welk type daadwerkelijk wordt verzonden, kan het ontvangende eindpunt niet vooraf bepalen van het gegevenscontract voor de verzonden gegevens. Dit is een speciaal geval van het eerste item: Elk gegevenscontract is afgeleid van de standaardwaarde, een leeg gegevenscontract waarvoor wordt gegenereerd Object.

  • Sommige typen, waaronder .NET Framework-typen, hebben leden die zich in een van de voorgaande drie categorieën bevinden. Gebruikt bijvoorbeeld HashtableObject voor het opslaan van de werkelijke objecten in de hash-tabel. Wanneer deze typen worden geserialiseerd, kan de ontvangende zijde niet vooraf bepalen welk gegevenscontract voor deze leden wordt gebruikt.

De klasse KnownTypeAttribute

Wanneer gegevens binnenkomen op een ontvangend eindpunt, probeert de WCF-runtime deserialiseren van de gegevens in een exemplaar van een CLR-type (Common Language Runtime). Het type dat wordt geïnstantieerd voor deserialisatie, wordt gekozen door eerst het binnenkomende bericht te inspecteren om het gegevenscontract te bepalen waaraan de inhoud van het bericht voldoet. De deserialisatie-engine probeert vervolgens een CLR-type te vinden dat een gegevenscontract implementeert dat compatibel is met de inhoud van het bericht. De set kandidaattypen die deserialisatie-engine tijdens dit proces toestaat, wordt de set 'bekende typen' van de deserializer genoemd.

Een manier om de deserialisatie-engine te laten weten over een type is door gebruik te maken van de KnownTypeAttribute. Het kenmerk kan niet worden toegepast op afzonderlijke gegevensleden, alleen op hele gegevenscontracttypen. Het kenmerk wordt toegepast op een buitenste type dat een klasse of een structuur kan zijn. In het meest eenvoudige gebruik geeft het toepassen van het kenmerk een type op als een 'bekend type'. Dit zorgt ervoor dat het bekende type deel uitmaakt van de set bekende typen wanneer een object van het buitenste type of een object waarnaar wordt verwezen via de leden wordt gedeserialiseerd. Er kunnen meer dan één KnownTypeAttribute kenmerk worden toegepast op hetzelfde type.

Bekende typen en primitieven

Primitieve typen, evenals bepaalde typen die worden behandeld als primitieven (bijvoorbeeld DateTime en XmlElement) zijn altijd 'bekend' en hoeven nooit via dit mechanisme te worden toegevoegd. Matrices van primitieve typen moeten echter expliciet worden toegevoegd. De meeste verzamelingen worden beschouwd als equivalent aan matrices. (Niet-generieke verzamelingen worden beschouwd als equivalent aan matrices van Object). Zie voorbeeld 4 voor een voorbeeld van het gebruik van primitieven, primitieve matrices en primitieve verzamelingen.

Notitie

In tegenstelling tot andere primitieve typen is de DateTimeOffset structuur standaard geen bekend type, dus moet deze handmatig worden toegevoegd aan de lijst met bekende typen.

Voorbeelden

In de volgende voorbeelden ziet u de KnownTypeAttribute klasse die wordt gebruikt.

Voorbeeld 1

Er zijn drie klassen met een overnamerelatie.

[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

De volgende CompanyLogo klasse kan worden geserialiseerd, maar kan niet worden gedeserialiseerd als het ShapeOfLogo lid is ingesteld op een CircleType of een TriangleType object, omdat de deserialisatie-engine geen typen herkent met de namen van gegevenscontractnamen 'Cirkel' of 'Driehoek'.

[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

De juiste manier om het CompanyLogo type te schrijven, wordt weergegeven in de volgende code.

[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

Wanneer het buitenste type CompanyLogo2 wordt gedeserialiseerd, weet de engine van deserialisatie over CircleType en TriangleType kan daarom overeenkomende typen vinden voor de gegevenscontracten 'Cirkel' en 'Driehoek'.

Voorbeeld 2

In het volgende voorbeeld wordt, ook al hebben beide CustomerTypeA en CustomerTypeB het Customer gegevenscontract, een exemplaar gemaakt wanneer CustomerTypeB een PurchaseOrder deserialisatie-engine wordt gedeserialiseerd, omdat alleen CustomerTypeB bekend is bij de deserialisatie-engine.

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

Voorbeeld 3

In het volgende voorbeeld Hashtable wordt de inhoud intern opgeslagen als Object. Als u een hash-tabel wilt deserialiseren, moet de deserialisatie-engine de set mogelijke typen kennen die zich daar kunnen voordoen. In dit geval weten we van tevoren dat alleen Book objecten Magazine worden opgeslagen in de Catalog, dus die worden toegevoegd met behulp van het KnownTypeAttribute kenmerk.

[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

Voorbeeld 4

In het volgende voorbeeld worden in een gegevenscontract een getal en een bewerking opgeslagen die moet worden uitgevoerd op het getal. Het Numbers gegevenslid kan een geheel getal, een matrix met gehele getallen of een List<T> geheel getal zijn dat gehele getallen bevat.

Let op

Dit werkt alleen aan de clientzijde als SVCUTIL.EXE wordt gebruikt om een WCF-proxy te genereren. SVCUTIL.EXE haalt metagegevens op uit de service, inclusief eventuele bekende typen. Zonder deze informatie kan een client deserialiseren van de typen niet.

[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

Dit is de toepassingscode.

// 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

Bekende typen, overname en interfaces

Wanneer een bekend type is gekoppeld aan een bepaald type met behulp van het KnownTypeAttribute kenmerk, wordt het bekende type ook gekoppeld aan alle afgeleide typen van dat type. Zie bijvoorbeeld de volgende code.

[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

De DoubleDrawing klasse vereist niet dat het KnownTypeAttribute kenmerk wordt gebruikt Square en Circle in het AdditionalShape veld, omdat de basisklasse (Drawing) deze kenmerken al heeft toegepast.

Bekende typen kunnen alleen worden gekoppeld aan klassen en structuren, niet aan interfaces.

Bekende typen met behulp van open algemene methoden

Het kan nodig zijn om een algemeen type toe te voegen als een bekend type. Een open algemeen type kan echter niet worden doorgegeven als parameter aan het KnownTypeAttribute kenmerk.

Dit probleem kan worden opgelost met behulp van een alternatief mechanisme: Schrijf een methode die een lijst met typen retourneert die moeten worden toegevoegd aan de verzameling bekende typen. De naam van de methode wordt vervolgens opgegeven als een tekenreeksargument voor het KnownTypeAttribute kenmerk vanwege een aantal beperkingen.

De methode moet bestaan op het type waarop het KnownTypeAttribute kenmerk wordt toegepast, moet statisch zijn, mag geen parameters accepteren en moet een object retourneren waaraan kan worden toegewezen IEnumerableType.

U kunt het KnownTypeAttribute kenmerk niet combineren met een methodenaam en KnownTypeAttribute kenmerken met werkelijke typen voor hetzelfde type. Bovendien kunt u niet meer dan één KnownTypeAttribute met een methodenaam toepassen op hetzelfde type.

Zie de volgende klasse.

[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

Het theDrawing veld bevat exemplaren van een algemene klasse ColorDrawing en een algemene klasse BlackAndWhiteDrawing, die beide overnemen van een algemene klasse Drawing. Normaal gesproken moeten beide worden toegevoegd aan bekende typen, maar het volgende is geen geldige syntaxis voor kenmerken.

// 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)))>  

Er moet dus een methode worden gemaakt om deze typen te retourneren. De juiste manier om dit type te schrijven, wordt vervolgens weergegeven in de volgende code.

[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

Aanvullende manieren om bekende typen toe te voegen

Daarnaast kunnen bekende typen worden toegevoegd via een configuratiebestand. Dit is handig wanneer u niet bepaalt welk type bekende typen nodig hebben voor de juiste deserialisatie, bijvoorbeeld wanneer u bibliotheken van derden gebruikt met WCF (Windows Communication Foundation).

In het volgende configuratiebestand ziet u hoe u een bekend type in een configuratiebestand opgeeft.

<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 het voorgaande configuratiebestand wordt een aangeroepen MyCompany.Library.Shape gegevenscontracttype gedeclareerd als MyCompany.Library.Circle een bekend type.

Zie ook