Tipos conocidos de contratos de datosData Contract Known Types

La clase KnownTypeAttribute le permite especificar, de antemano, los tipos que deberían tenerse en cuenta durante la deserialización.The KnownTypeAttribute class allows you to specify, in advance, the types that should be included for consideration during deserialization. Para ver un ejemplo ilustrativo, consulte el ejemplo Known Types .For a working example, see the Known Types example.

Normalmente, al pasar parámetros y valores devueltos entre un cliente y un servicio, ambos extremos comparten todos los contratos de datos de los datos que se van a transmitir.Normally, when passing parameters and return values between a client and a service, both endpoints share all of the data contracts of the data to be transmitted. Sin embargo, éste no es el caso en las siguientes circunstancias:However, this is not the case in the following circumstances:

  • El contrato de datos enviados se deriva del contrato de datos esperados.The sent data contract is derived from the expected data contract. Para obtener más información, vea la sección acerca de la herencia en la equivalencia del contrato de datos.For more information, see the section about inheritance in Data Contract Equivalence). En ese caso, los datos transmitidos no tienen el mismo contrato de datos que espera el extremo receptor.In that case, the transmitted data does not have the same data contract as expected by the receiving endpoint.

  • El tipo declarado de la información que se va a transmitir es una interfaz, en lugar de una clase, estructura o enumeración.The declared type for the information to be transmitted is an interface, as opposed to a class, structure, or enumeration. En consecuencia, no se puede saber por adelantado qué tipo que implementa la interfaz se envía realmente y, por consiguiente, el extremo receptor no puede determinar de antemano el contrato de datos para los datos transmitidos.Therefore, it cannot be known in advance which type that implements the interface is actually sent and therefore, the receiving endpoint cannot determine in advance the data contract for the transmitted data.

  • El tipo declarado de la información que se va a transmitir es Object.The declared type for the information to be transmitted is Object. Puesto que cada tipo hereda de Object, y no se puede conocer de antemano qué tipo se envía realmente, el extremo receptor no puede determinar de antemano el contrato de datos para los datos transmitidos.Because every type inherits from Object, and it cannot be known in advance which type is actually sent, the receiving endpoint cannot determine in advance the data contract for the transmitted data. Este es un caso especial del primer elemento: Cada contrato de datos se deriva del valor predeterminado, un contrato de datos en blanco que Objectse genera para.This is a special case of the first item: Every data contract derives from the default, a blank data contract that is generated for Object.

  • Algunos tipos, entre los que se incluyen tipos .NET Framework, tienen miembros que se encuentran en una de las tres categorías anteriores.Some types, which include .NET Framework types, have members that are in one of the preceding three categories. Por ejemplo, Hashtable utiliza Object para almacenar los objetos reales en la tabla hash.For example, Hashtable uses Object to store the actual objects in the hash table. Al serializar estos tipos, el lado receptor no puede determinar de antemano el contrato de datos de estos miembros.When serializing these types, the receiving side cannot determine in advance the data contract for these members.

La clase KnownTypeAttributeThe KnownTypeAttribute Class

Cuando los datos llegan a un extremo receptor, el tiempo de ejecución de WCF intenta deserializar los datos en una instancia de un tipo Common Language Runtime (CLR).When data arrives at a receiving endpoint, the WCF runtime attempts to deserialize the data into an instance of a common language runtime (CLR) type. El tipo del que se crea una instancia para la deserialización se elige inspeccionando primero el mensaje entrante para determinar el contrato de datos al que se ajusta el contenido del mensaje.The type that is instantiated for deserialization is chosen by first inspecting the incoming message to determine the data contract to which the contents of the message conform. El motor de deserialización intenta a continuación encontrar un tipo CLR que implemente un contrato de datos compatible con el contenido del mensaje.The deserialization engine then attempts to find a CLR type that implements a data contract compatible with the message contents. El conjunto de tipos de candidatos que admite el motor de deserialización durante este proceso se conoce como el conjunto del deserializador de "tipos conocidos."The set of candidate types that the deserialization engine allows for during this process is referred to as the deserializer's set of "known types."

Una manera de permitir al motor de deserialización saber sobre un tipo consiste en utilizar el KnownTypeAttribute.One way to let the deserialization engine know about a type is by using the KnownTypeAttribute. El atributo no se puede aplicar a miembros de datos individuales, solo a tipos de contrato de datos enteros.The attribute cannot be applied to individual data members, only to whole data contract types. El atributo se aplica a un tipo exterior que puede ser una clase o una estructura.The attribute is applied to an outer type that can be a class or a structure. En su uso más básico, al aplicar el atributo, se especifica un tipo como "tipo conocido".In its most basic usage, applying the attribute specifies a type as a "known type." Esto hace que el tipo conocido forme parte del conjunto de tipos conocidos siempre que se deserialice un objeto del tipo exterior o cualquier objeto al que se hace referencia a través de sus miembros.This causes the known type to be a part of the set of known types whenever an object of the outer type or any object referred to through its members is being deserialized. Se puede aplicar más de un atributo KnownTypeAttribute al mismo tipo.More than one KnownTypeAttribute attribute can be applied to the same type.

Tipos conocidos y primitivosKnown Types and Primitives

Los tipos primitivos, así como ciertos tipos tratados como primitivos (como, por ejemplo, DateTime y XmlElement) son siempre “conocidos” y nunca tienen que agregarse mediante este mecanismo.Primitive types, as well as certain types treated as primitives (for example, DateTime and XmlElement) are always "known" and never have to be added through this mechanism. Sin embargo, las matrices de tipos primitivos tienen que agregarse explícitamente.However, arrays of primitive types have to be added explicitly. La mayoría de las colecciones se consideran equivalentes a las matrices.Most collections are considered equivalent to arrays. (Las colecciones no genéricas se consideran equivalentes a las matrices de Object).(Non-generic collections are considered equivalent to arrays of Object). Para obtener un ejemplo del uso de primitivos, matrices de primitivos y colecciones de primitivos, vea el Ejemplo 4.For an example of the using primitives, primitive arrays, and primitive collections, see Example 4.

Nota

A diferencia de otros tipos primitivos, la estructura DateTimeOffset no es un tipo conocido de forma predeterminada, por lo que debe agregarse manualmente a la lista de tipos conocidos.Unlike other primitive types, the DateTimeOffset structure is not a known type by default, so it must be manually added to the list of known types.

EjemplosExamples

En los siguientes ejemplos se muestra la clase KnownTypeAttribute en uso.The following examples show the KnownTypeAttribute class in use.

Ejemplo 1Example 1

Hay tres clases con una relación de herencia.There are three classes with an inheritance relationship.

[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

Se puede serializar la siguiente clase CompanyLogo , pero no se puede deserializar si el miembro ShapeOfLogo está establecido en un objeto CircleType o TriangleType , porque el motor de deserialización no reconoce ningún tipo con nombres de contrato de datos "Circle" o "Triangle".The following CompanyLogo class can be serialized, but cannot be deserialized if the ShapeOfLogo member is set to either a CircleType or a TriangleType object, because the deserialization engine does not recognize any types with data contract names "Circle" or "Triangle."

[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

La forma correcta de escribir el tipo CompanyLogo se muestra en el siguiente código.The correct way to write the CompanyLogo type is shown in the following 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

Cuando quiera que se deserializa el tipo exterior CompanyLogo2 , el motor de deserialización sabe de CircleType y TriangleType y, por consiguiente, puede encontrar tipos coincidentes para los contratos de datos "Circle" y "Triangle".Whenever the outer type CompanyLogo2 is being deserialized, the deserialization engine knows about CircleType and TriangleType and, therefore, is able to find matching types for the "Circle" and "Triangle" data contracts.

Ejemplo 2Example 2

En el siguiente ejemplo , aunque CustomerTypeA y CustomerTypeB tienen el contrato de datos Customer , se crea una instancia de CustomerTypeB siempre que se deserializa una PurchaseOrder , porque el motor de deserialización solo conoce al CustomerTypeB .In the following example, even though both CustomerTypeA and CustomerTypeB have the Customer data contract, an instance of CustomerTypeB is created whenever a PurchaseOrder is deserialized, because only CustomerTypeB is known to the deserialization 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

Ejemplo 3Example 3

En el siguiente ejemplo, una Hashtable almacena internamente su contenido como Object.In the following example, a Hashtable stores its contents internally as Object. Para deserializar correctamente una tabla hash, el motor de deserialización debe conocer el conjunto de tipos posibles que se pueden dar.To successfully deserialize a hash table, the deserialization engine must know the set of possible types that can occur there. En este caso, conocemos de antemano que solo los objetos Book y Magazine se almacenan en Catalog, por lo que aquellos se agregan utilizando el atributo KnownTypeAttribute .In this case, we know in advance that only Book and Magazine objects are stored in the Catalog, so those are added using the KnownTypeAttribute attribute.

[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

Ejemplo 4Example 4

En el siguiente ejemplo, un contrato de datos almacena un número y una operación que se va a realizar sobre el número.In the following example, a data contract stores a number and an operation to perform on the number. El miembro de datos Numbers puede ser un entero, una matriz de enteros, o una List<T> que contenga enteros.The Numbers data member can be an integer, an array of integers, or a List<T> that contains integers.

Precaución

Esto solo funcionará en el lado del cliente si se usa SVCUTIL.EXE para generar un proxy WCF.This will only work on the client side if SVCUTIL.EXE is used to generate a WCF proxy. SVCUTIL.EXE recupera metadatos del servicio, incluyendo los tipos conocidos.SVCUTIL.EXE retrieves metadata from the service including any known types. Sin esta información, un cliente no podrá deserializar los tipos.Without this information a client will not be able to deserialize the types.

[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

Éste es el código de aplicación.This is the application code.

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

Herencia, interfaces y tipos conocidosKnown Types, Inheritance, and Interfaces

Cuando un tipo conocido está asociado a un tipo determinado mediante el atributo KnownTypeAttribute , el tipo conocido también está asociado a todos los tipos derivados de ese tipo.When a known type is associated with a particular type using the KnownTypeAttribute attribute, the known type is also associated with all of the derived types of that type. Por ejemplo, vea el siguiente código:For example, see the following 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

La clase DoubleDrawing no exige al atributo KnownTypeAttribute que utilice Square y Circle en el campo AdditionalShape , porque ya se han aplicado esos atributos a la clase base (Drawing).The DoubleDrawing class does not require the KnownTypeAttribute attribute to use Square and Circle in the AdditionalShape field, because the base class (Drawing) already has these attributes applied.

Los tipos conocidos solo pueden estar asociados a clases y estructuras, no a interfaces.Known types can be associated only with classes and structures, not interfaces.

Tipos conocidos utilizando métodos genéricos abiertosKnown Types Using Open Generic Methods

Puede que sea necesario agregar un tipo genérico como un tipo conocido.It may be necessary to add a generic type as a known type. Sin embargo, un tipo genérico abierto no se puede pasar como un parámetro al atributo KnownTypeAttribute .However, an open generic type cannot be passed as a parameter to the KnownTypeAttribute attribute.

Este problema se puede resolver mediante el uso de un mecanismo alternativo: Escriba un método que devuelva una lista de tipos que se van a agregar a la colección de tipos conocidos.This problem can be solved by using an alternative mechanism: Write a method that returns a list of types to add to the known types collection. El nombre del método se especifica a continuación como un argumento de cadena al atributo KnownTypeAttribute debido a algunas restricciones.The name of the method is then specified as a string argument to the KnownTypeAttribute attribute due to some restrictions.

El método debe existir en el tipo al que se aplica el atributo KnownTypeAttribute , debe ser estático, no debe aceptar parámetros y debe devolver un objeto que se pueda asignar a IEnumerable de Type.The method must exist on the type to which the KnownTypeAttribute attribute is applied, must be static, must accept no parameters, and must return an object that can be assigned to IEnumerable of Type.

No puede combinar el atributo KnownTypeAttribute con un nombre de método y atributos KnownTypeAttribute con tipos reales en el mismo tipo.You cannot combine the KnownTypeAttribute attribute with a method name and KnownTypeAttribute attributes with actual types on the same type. Es más, no puede aplicar más de un KnownTypeAttribute con un nombre de método al mismo tipo.Furthermore, you cannot apply more than one KnownTypeAttribute with a method name to the same type.

Vea la siguiente clase.See the following class.

[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

El campo theDrawing contiene instancias de una clase genérica ColorDrawing y una clase genérica BlackAndWhiteDrawing, las cuales heredan de una clase genérica Drawing.The theDrawing field contains instances of a generic class ColorDrawing and a generic class BlackAndWhiteDrawing, both of which inherit from a generic class Drawing. Normalmente, ambos se deben agregar a los tipos conocidos, pero la siguiente sintaxis no es una sintaxis válida para atributos.Normally, both must be added to known types, but the following is not valid syntax for attributes.

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

De este modo, se debe crear un método para devolver estos tipos.Thus, a method must be created to return these types. La forma correcta de escribir este tipo, entonces, se muestra en el siguiente código.The correct way to write this type, then, is shown in the following 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

Maneras adicionales de agregar tipos conocidosAdditional Ways to Add Known Types

Además, los tipos conocidos se pueden agregar a través de un archivo de configuración.Additionally, known types can be added through a configuration file. Esto resulta útil cuando no se controla el tipo que requiere tipos conocidos para la deserialización correcta, como cuando se usan bibliotecas de tipos de terceros con Windows Communication Foundation (WCF).This is useful when you do not control the type that requires known types for proper deserialization, such as when using third-party type libraries with Windows Communication Foundation (WCF).

En el archivo de configuración siguiente se observa cómo se especifica un tipo conocido en un archivo de configuración.The following configuration file shows how to specify a known type in a configuration file.

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

En el archivo de configuración anterior, se declara que un tipo de contrato de datos denominado MyCompany.Library.Shape tiene MyCompany.Library.Circle como tipo conocido.In the preceding configuration file a data contract type called MyCompany.Library.Shape is declared to have MyCompany.Library.Circle as a known type.

Vea tambiénSee also