数据协定已知类型

KnownTypeAttribute 类允许您预先指定应该在反序列化期间包括在考虑范围内的类型。有关工作示例,请参见已知类型示例。

通常,在客户端和服务之间传递参数和返回值时,这两个终结点共享要传输的数据的所有数据协定。但是,在以下情况下并非如此:

  • 已发送的数据协定源自预期的数据协定(有关更多信息,请参见数据协定等效性中有关继承的一节)。在该情况下,传输的数据没有与接收终结点所预期相同的数据协定。

  • 要传输的信息的声明类型是接口,而非类、结构或枚举。因此,无法预先知道实际发送了实现接口的哪个类型,接收终结点就无法预先确定已传输数据的数据协定。

  • 要传输的信息的声明类型是 Object。由于每个类型都继承自 Object,而且无法预先知道实际发送了哪个类型,因此接收终结点无法预先确定已传输数据的数据协定。这是第一个项的特殊情况:每个数据协定都源自为 Object 生成的默认空数据协定。

  • 某些类型(包括 .NET Framework 类型)具有上述三种类别之一中的成员。例如,Hashtable 使用 Object 在哈希表中存储实际对象。在序列化这些类型时,接收方无法预先确定这些成员的数据协定。

KnownTypeAttribute 类

在数据到达接收终结点时,WCF 运行库尝试将数据反序列化为公共语言运行库 (CLR) 类型的实例。通过首先检查传入消息选择为反序列化而实例化的类型,以确定消息内容遵循的数据协定。然后反序列化引擎尝试查找实现与消息内容兼容的数据协定的 CLR 类型。反序列化引擎在此过程中允许的侯选类型集称为反序列化程序的“已知类型”集。

让反序列化引擎了解某个类型的一种方法是使用 KnownTypeAttribute。不能将属性应用于单个数据成员,只能将它应用于整个数据协定类型。将属性应用于可能为类或结构的“外部类型”**。在其最基本的用法中,应用属性会将类型指定为“已知类型”。只要反序列化外部类型的对象或通过其成员引用的任何对象,这就会导致已知类型成为已知类型集的一部分。可以将多个 KnownTypeAttribute 属性应用于同一类型。

已知类型和基元

基元类型以及被视为基元的某些类型(例如,DateTimeXmlElement)始终是“已知”的,且从来不必通过此机制进行添加。但是,必须显式添加基元类型的数组。大多数集合被视为等效于数组。(非泛型集合被视为等效于 Object 的数组)。有关使用基元、基元数组和基元集合的示例,请参见示例 4。

ms730167.note(zh-cn,VS.100).gif注意:
与其他基元类型不同,DateTimeOffset 结构默认情况下不是已知类型,因此必须将它手动添加到已知类型列表。

示例

下面的示例说明如何使用 KnownTypeAttribute 类。

示例 1

有三个具有继承关系的类。

<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
[DataContract]
public class Shape { }

[DataContract(Name = "Circle")]
public class CircleType : Shape { }

[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }

如果 ShapeOfLogo 成员设置为 CircleTypeTriangleType 对象,则可以序列化下面的 CompanyLogo 类,而不能对其进行反序列化,因为反序列化引擎无法识别具有数据协定名称“Circle”或“Triangle”的任何类型。

<DataContract()> _
Public Class CompanyLogo
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class
[DataContract]
public class CompanyLogo
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;
}

在下面的代码中演示了编写 CompanyLogo 类型的正确方法。

<DataContract(), KnownType(GetType(CircleType)), KnownType(GetType(TriangleType))> _
Public Class CompanyLogo2
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;

}

只要反序列化外部类型 CompanyLogo2,反序列化引擎就会了解有关 CircleTypeTriangleType,因此能够查找“Circle”和“Triangle”数据协定的匹配类型。

示例 2

在下面的示例中,尽管 CustomerTypeACustomerTypeB 都具有 Customer 数据协定,但是只要反序列化 PurchaseOrder 就会创建 CustomerTypeB 的实例,因为只有 CustomerTypeB 对反序列化引擎是已知的。

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

示例 3

在下面的示例中,Hashtable 将其内容在内部存储为 Object。若要成功反序列化哈希表,反序列化引擎必须知道那里可能出现的一组可能类型。在这种情况下,我们预先知道只有 BookMagazine 对象存储在 Catalog 中,因此使用 KnownTypeAttribute 属性添加它们。

<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
[DataContract]
public class Book { }

[DataContract]
public class Magazine { }

[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
    [DataMember]
    System.Collections.Hashtable theCatalog;
}

示例 4

在下面的示例中,数据协定存储一个数字和要对该数字执行的操作。Numbers 数据成员可以是整数、整数数组或包含整数的 List

ms730167.Caution(zh-cn,VS.100).gif警告:
仅当使用 SVCUTIL.EXE 来生成 WCF 代理时,此方法才能在客户端使用。SVCUTIL.EXE 从包含任何已知类型的服务中检索元数据。如果没有此信息,客户端将不能反序列化该类型。

<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
[DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
    private object numberValue;
    [DataMember]
    public object Numbers
    {
        get { return numberValue; }
        set { numberValue = value; }
    }
    //[DataMember]
    //public Operation Operation;
}

这是应用程序代码。

' 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
// 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;
}

已知类型、继承和接口

使用 KnownTypeAttribute 属性将已知类型与特定类型关联时,已知类型也与该类型的所有派生类型关联。例如,请参见下面的代码。

<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
[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;
}

DoubleDrawing 类无需 KnownTypeAttribute 属性即可在 AdditionalShape 字段中使用 SquareCircle,因为基类 (Drawing) 已经应用这些属性。

已知类型只能与类和结构关联,而不能与接口关联。

使用开放式泛型方法的已知类型

可能需要将泛型类型作为已知类型添加。但是,不能将开放式泛型类型作为参数传递到 KnownTypeAttribute 属性。

通过使用替代机制可以解决此问题:编写一个返回要添加到已知类型集合的类型列表的方法。然后将方法名称指定为 KnownTypeAttribute 属性的字符串参数(由于某些限制所致)。

方法必须存在于应用 KnownTypeAttribute 属性的类型上,不得接受参数,且必须返回可以分配给 TypeIEnumerable 的对象。

不能将具有方法名称的 KnownTypeAttribute 属性与实际类型在同一类型上的 KnownTypeAttribute 属性组合在一起。此外,不能将具有方法名称的多个 KnownTypeAttribute 应用于同一类型。

请参见下面的类。

<DataContract()> _
Public Class DrawingRecord(Of T)
    <DataMember()> _
    Private theData As T
    <DataMember()> _
    Private theDrawing As GenericDrawing(Of T)
End Class
[DataContract]
public class DrawingRecord<T>
{
    [DataMember]
    private T theData;
    [DataMember]
    private GenericDrawing<T> theDrawing;
}

theDrawing 字段包含泛型类 ColorDrawing 和泛型类 BlackAndWhiteDrawing 的实例,这两个泛型类都是从泛型类 Drawing 继承的。通常,必须将它们添加到已知类型,但下面不是有效的属性语法。

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

因此,必须创建一个方法以返回这些类型。在下面的代码中演示了编写此类型的正确方法。

<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
[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;
    }
}

添加已知类型的其他方法

也可以将类型添加到通过 DataContractSerializerKnownTypes 属性访问的 ReadOnlyCollection

此外,可以通过配置文件添加已知类型。在不控制需要已知类型才能正确反序列化的类型时,这是很有用的,如将第三方类型库与 Windows Communication Foundation (WCF) 一起使用时。

下面的配置文件演示如何在配置文件中指定已知类型。

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

在前面的配置文件中,名为 MyCompany.Library.Shape 的数据协定类型被声明具有已知类型 MyCompany.Library.Circle

另请参见

任务

已知类型

参考

KnownTypeAttribute
Hashtable
Object
DataContractSerializer
KnownTypes

概念

数据协定等效性
设计服务协定