导入架构以生成类

若要从可用于 Windows Communication Foundation (WCF)的架构生成类,请使用该 XsdDataContractImporter 类。 本主题介绍过程和变体。

导入过程

架构导入过程从 XmlSchemaSet 开始并生成一个 CodeCompileUnit

XmlSchemaSet它是 .NET Framework 架构对象模型 (SOM) 的一部分,表示一组 XML 架构定义语言 (XSD) 架构文档。 若要从一组 XSD 文档创建 XmlSchemaSet 对象,请将每个文档反序列化为一个 XmlSchema 对象(使用 XmlSerializer该对象),并将这些对象添加到新的 XmlSchemaSet对象中。

它是 CodeCompileUnit .NET Framework 的代码文档对象模型(CodeDOM)的一部分,它以抽象方式表示 .NET Framework 代码。 若要从 a CodeCompileUnit生成实际代码,请使用类的 CodeDomProvider 子类,例如 CSharpCodeProviderVBCodeProvider 类。

导入架构

  1. 创建XsdDataContractImporter的实例。

  2. 可选。 在构造函数中传递一个 CodeCompileUnit。 架构导入期间生成的类型将添加到此 CodeCompileUnit 实例,而不是从空白 CodeCompileUnit开始。

  3. 可选。 调用 CanImport 方法之一。 该方法确定给定架构是否为有效的数据协定架构,并且可以导入。 CanImport 方法与 Import 方法(下一步)的重载相同。

  4. 调用重载的 Import 方法之一,例如 Import(XmlSchemaSet) 方法。

    最简单的重载采用 XmlSchemaSet 并导入在该架构集中找到的所有类型,包括匿名类型。 使用其他重载可以指定 XSD 类型或要导入的类型列表(以 XmlQualifiedNameXmlQualifiedName 对象集合的形式)。 在这种情况下,仅导入指定的类型。 存在一个重载,它采用 XmlSchemaElement 导入 XmlSchemaSet 之外的特定元素,以及它的关联类型(无论类型是否为匿名)。 此重载返回一个 XmlQualifiedName值,表示为此元素生成的类型的数据协定名称。

    多次调用Import方法会导致多个项被添加到同一CodeCompileUnit中。 如果某个类型已经存在于 CodeCompileUnit 中,则不会再在其中生成该类型。 在同一Import对象上多次调用XsdDataContractImporter,而不是使用多个XsdDataContractImporter对象。 这是避免生成重复类型的建议方法。

    注释

    如果在导入期间出现故障,则 CodeCompileUnit 状态将不可预知。 使用导入失败产生的CodeCompileUnit可能会使您面临安全漏洞。

  5. 通过 CodeCompileUnit 属性访问 CodeCompileUnit

导入选项:自定义生成的类型

可以将OptionsXsdDataContractImporter属性设置为类ImportOptions的一个实例,以控制导入过程的各个方面。 许多选项直接影响生成的类型。

控制访问级别(GenerateInternal 或 /internal 开关)

这对应于 ServiceModel 元数据实用工具 (Svcutil.exe) 中的 /internal 开关

通常,公共类型是从架构生成的,具有专用字段和匹配的公共数据成员属性。 若要改为生成内部类型,请将 GenerateInternal 属性设置为 true.

以下示例演示当 GenerateInternal 属性设置为 true. 时,架构被转换为内部类。

[DataContract]
internal partial class Vehicle : IExtensibleDataObject
{
    private int yearField;
    private string colorField;

    [DataMember]
    internal int year
    {
        get { return this.yearField; }
        set { this.yearField = value; }
    }
    [DataMember]
    internal string color
    {
        get { return this.colorField; }
        set { this.colorField = value; }
    }

    private ExtensionDataObject extensionDataField;
    public ExtensionDataObject ExtensionData
    {
        get { return this.extensionDataField; }
        set { this.extensionDataField = value; }
    }
}
Class Vehicle
    Implements IExtensibleDataObject
    Private yearField As Integer
    Private colorField As String

    <DataMember()> _
    Friend Property year() As Integer
        Get
            Return Me.yearField
        End Get
        Set
            Me.yearField = value
        End Set
    End Property

    <DataMember()> _
    Friend Property color() As String
        Get
            Return Me.colorField
        End Get
        Set
            Me.colorField = value
        End Set
    End Property
    Private extensionDataField As ExtensionDataObject

    Public Property ExtensionData() As ExtensionDataObject _
        Implements IExtensibleDataObject.ExtensionData
        Get
            Return Me.extensionDataField
        End Get
        Set(ByVal value As ExtensionDataObject)
            Me.extensionDataField = value
        End Set
    End Property
End Class

控制命名空间(Namespaces 或 /namespace 开关)

这对应于 工具中的 /namespace 开关Svcutil.exe

通常,根据 数据协定架构参考中所述的映射,从架构生成的类型将生成到 .NET Framework 命名空间中,每个 XSD 命名空间对应于特定的 .NET Framework 命名空间。 可以通过Namespaces属性将此映射自定义为Dictionary<TKey,TValue>。 如果在字典中找到给定的 XSD 命名空间,则匹配的 .NET Framework 命名空间也会从字典中提取。

例如,请考虑以下架构。

<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
  <xs:complexType name="Vehicle">
    <!-- details omitted... -->
  </xs:complexType>
</xs:schema>

以下示例使用 Namespaces 该属性将 http://schemas.contoso.com/carSchema 命名空间映射到“Contoso.Cars”。

XsdDataContractImporter importer = new XsdDataContractImporter();
importer.Options.Namespaces.Add(new KeyValuePair<string, string>("http://schemas.contoso.com/carSchema", "Contoso.Cars"));
Dim importer As New XsdDataContractImporter
importer.Options.Namespaces.Add(New KeyValuePair(Of String, String)("http://schemas.contoso.com/carSchema", "Contoso.Cars"))

添加 SerializableAttribute(GenerateSerializable 或 /serializable 开关)

这对应于 工具中的 /serializable 开关Svcutil.exe

有时,对于从架构生成的类型来说,能够与 .NET Framework 运行时序列化引擎一起使用十分重要。 在使用 .NET Framework 远程处理类型时,它非常有用。 若要启用此功能,除了常规属性外,还必须将 SerializableAttribute 属性应用于生成的类型 DataContractAttribute 。 如果 GenerateSerializable 导入选项设置为 true.,则自动生成该属性。

下面的示例演示在 Vehicle 导入选项设置为 GenerateSerializable 时生成的 true 类。

[DataContract]
[Serializable]
public partial class Vehicle : IExtensibleDataObject
{
    // Code not shown.
    public ExtensionDataObject ExtensionData
    {
        get
        {
            throw new Exception("The method or operation is not implemented.");
        }
        set
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
<DataContract(), Serializable()> _
Partial Class Vehicle
    Implements IExtensibleDataObject
    Private extensionDataField As ExtensionDataObject

    ' Code not shown.

    Public Property ExtensionData() As ExtensionDataObject _
        Implements IExtensibleDataObject.ExtensionData
        Get
            Return Me.extensionDataField
        End Get
        Set(ByVal value As ExtensionDataObject)
            Me.extensionDataField = value
        End Set
    End Property

End Class

添加数据绑定支持(EnableDataBinding 或 /enableDataBinding 开关)

这对应于 Svcutil.exe 工具上的 /enableDataBinding 开关。

有时,你可能希望将架构生成的类型绑定到图形用户界面组件,以便对这些类型的实例进行任何更新都会自动更新 UI。 XsdDataContractImporter 可以生成实现 INotifyPropertyChanged 接口的类型,以使任何属性更改触发事件。 如果要生成用于支持此接口(如 Windows Presentation Foundation(WPF)的客户端 UI 编程环境的类型,请将 EnableDataBinding 属性设置为 true 启用此功能。

下面的示例演示在 Vehicle 设置为 EnableDataBinding 时生成的 true 类。

[DataContract]
public partial class Vehicle : IExtensibleDataObject, INotifyPropertyChanged
{
    private int yearField;
    private string colorField;

    [DataMember]
    public int year
    {
        get { return this.yearField; }
        set
        {
            if (this.yearField.Equals(value) != true)
            {
                this.yearField = value;
                this.RaisePropertyChanged("year");
            }
        }
    }
    [DataMember]
    public string color
    {
        get { return this.colorField; }
        set
        {
            if (this.colorField.Equals(value) != true)
            {
                this.colorField = value;
                this.RaisePropertyChanged("color");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged =
this.PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this,
new PropertyChangedEventArgs(propertyName));
        }
    }

    private ExtensionDataObject extensionDataField;
    public ExtensionDataObject ExtensionData
    {
        get { return this.extensionDataField; }
        set { this.extensionDataField = value; }
    }
}
Partial Class Vehicle
    Implements IExtensibleDataObject, INotifyPropertyChanged
    Private yearField As Integer
    Private colorField As String

    <DataMember()> _
    Public Property year() As Integer
        Get
            Return Me.yearField
        End Get
        Set
            If Me.yearField.Equals(value) <> True Then
                Me.yearField = value
                Me.RaisePropertyChanged("year")
            End If
        End Set
    End Property

    <DataMember()> _
    Public Property color() As String
        Get
            Return Me.colorField
        End Get
        Set
            If Me.colorField.Equals(value) <> True Then
                Me.colorField = value
                Me.RaisePropertyChanged("color")
            End If
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler _
      Implements INotifyPropertyChanged.PropertyChanged

    Private Sub RaisePropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, _
         New PropertyChangedEventArgs(propertyName))
    End Sub

    Private extensionDataField As ExtensionDataObject

    Public Property ExtensionData() As ExtensionDataObject _
        Implements IExtensibleDataObject.ExtensionData
        Get
            Return Me.extensionDataField
        End Get
        Set(ByVal value As ExtensionDataObject)
            Me.extensionDataField = value
        End Set
    End Property

End Class

导入选项:选择集合类型

XML 中的两种特殊模式表示项集合:项列表以及一项与另一项之间的关联。 下面是字符串列表的示例。

<People>
  <person>Alice</person>
  <person>Bob</person>
  <person>Charlie</person>
</People>

下面是字符串与整数(city namepopulation)之间的关联示例。

<Cities>
  <city>
    <name>Auburn</name>
    <population>40000</population>
  </city>
  <city>
    <name>Bellevue</name>
    <population>80000</population>
  </city>
  <city>
    <name>Cedar Creek</name>
    <population>10000</population>
  </city>
</Cities>

注释

还可以将任何关联视为列表。 例如,可以将上述关联视为恰好有两个字段(字符串字段和整数字段)的复杂 city 对象列表。 这两种模式在 XSD 架构中都有表示形式。 无法区分列表和关联,因此,除非架构中存在特定于 WCF 的特殊注释,否则此类模式始终被视为列表。 批注指示给定模式表示关联。 有关详细信息,请参阅 数据协定架构参考

通常,列表作为集合数据协定导入,该协定派生自泛型列表或 .NET Framework 数组,具体取决于架构是否遵循集合的标准命名模式。 这在数据协定中的集合类型中有更详细的描述。 通常将关联作为 Dictionary<TKey,TValue> 或派生自字典对象的集合数据协定导入。 例如,请考虑以下架构。

<xs:complexType name="Vehicle">
  <xs:sequence>
    <xs:element name="year" type="xs:int"/>
    <xs:element name="color" type="xs:string"/>
    <xs:element name="passengers" type="people"/>
  </xs:sequence>
</xs:complexType>
<xs:complexType name="people">
  <xs:sequence>
    <xs:element name="person" type="xs:string" maxOccurs="unbounded" />
  </xs:sequence>
</xs:complexType>

这将按如下方式导入(为了可读性,将显示字段而不是属性)。

[DataContract]
public partial class Vehicle : IExtensibleDataObject
{
    [DataMember] public int yearField;
    [DataMember] public string colorField;
    [DataMember] public people passengers;

    // Other code not shown.

    public ExtensionDataObject ExtensionData
    {
        get
        {
            throw new Exception("The method or operation is not implemented.");
        }
        set
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
[CollectionDataContract(ItemName = "person")]
public class people : List<string> { }
Public Partial Class Vehicle
    Implements IExtensibleDataObject

    <DataMember()> _
    Public yearField As Integer
    <DataMember()> _
    Public colorField As String
    <DataMember()> _
    Public passengers As people

    ' Other code not shown.

    Public Property ExtensionData() As ExtensionDataObject _
    Implements IExtensibleDataObject.ExtensionData
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
        Set
            Throw New Exception("The method or operation is not implemented.")
        End Set
    End Property
End Class

<CollectionDataContract(ItemName:="person")> _
Public Class people
    Inherits List(Of String)
End Class

可以自定义为此类架构模式生成的集合类型。 例如,可能需要生成派生自 BindingList<T> 而非 List<T> 类的集合,以将此类型绑定到列表框并在集合内容更改时使其自动更新。 为此,请将 ReferencedCollectionTypes 类的属性 ImportOptions 设置为要使用的集合类型列表(后称为引用的类型)。 导入任何集合时,会扫描引用的集合类型的此列表,如果找到该集合,则使用最匹配的集合。 关联仅与实现泛型或非泛型 IDictionary 接口的类型匹配,而列表与任何受支持的集合类型匹配。

例如,如果该 ReferencedCollectionTypes 属性设置为一个 BindingList<T>,那么上述示例中的 people 类型将按如下所示生成。

[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
    Inherits BindingList(Of String)

封闭泛型被视为最佳匹配。 例如,如果BindingList(Of Integer)ArrayList类型传递给引用类型的集合,则在架构中找到的任何整数列表都会被导入为BindingList(Of Integer)。 任何其他列表(例如,List(Of String))都导入为ArrayList

如果将实现泛型 IDictionary 接口的类型添加到引用类型的集合中,则其类型参数必须完全打开或完全关闭。

不允许重复。 例如,不能同时向引用的类型添加 a List(Of Integer) 和 a Collection(Of Integer) 。 这样就不可能确定在架构中找到整数列表时应使用哪些值。 只有当架构中存在一种类型暴露出重复项问题时,才会检测到重复项。 例如,如果导入的架构不包含整数列表,则允许在引用的类型集合中同时存在List(Of Integer)Collection(Of Integer),但两者都不会产生任何影响。

引用的集合类型机制同样适用于复杂类型的集合(包括其他集合的集合),而不仅仅是基元集合。

ReferencedCollectionTypes 属性对应于 SvcUtil.exe 工具上的 /collectionType 开关。 请注意,若要引用多个集合类型,必须多次指定 /collectionType 开关。 如果类型不在 MsCorLib.dll 中,也必须使用 /reference 开关引用其程序集

导入选项:引用现有类型

有时,架构中的类型对应于现有的 .NET Framework 类型,无需从头开始生成这些类型。 (本部分仅适用于非集合类型。有关集合类型,请参阅前面的部分。

例如,您可能有一个标准公司范围内的“Person”数据协定类型,通常在表示人员时需要使用它。 每当某些服务使用此类型,并且其架构出现在服务元数据中时,你可能希望在导入此架构时重复使用现有 Person 类型,而不是为每个服务生成一个新类型。

为此,将要重用的 .NET Framework 类型列表传递给 ReferencedTypes 属性返回到 ImportOptions 类上的集合。 如果其中任一类型具有与架构类型的名称和命名空间匹配的数据协定名称和命名空间,则执行结构比较。 如果确定类型同时具有匹配的名称和匹配结构,则会重复使用现有的 .NET Framework 类型,而不是生成新的类型。 如果仅名称匹配而构造不匹配,则引发异常。 请注意,引用类型时,不允许进行版本控制(例如,不能添加新的可选数据成员)。 结构必须完全匹配。

向引用的类型集合添加多个具有相同数据协定名称和命名空间的类型是合法的,前提是不导入具有该名称和命名空间的架构类型。 这样您就可以轻松地将程序集中的所有类型添加到集合中,而无需担心在架构中并未实际出现的类型重复项。

ReferencedTypes 属性在 Svcutil.exe 工具的某些操作模式中对应 /reference 开关。

注释

使用 Svcutil.exe 或(在 Visual Studio 中) 添加服务引用 工具时,将自动引用 MsCorLib.dll 中的所有类型。

导入选项:将 Non-DataContract 架构作为 IXmlSerializable 类型导入

XsdDataContractImporter 支持有限的模式子集。 如果存在不受支持的架构构造(例如 XML 属性),则导入尝试会失败,但出现异常。 但是,将属性设置为ImportXmlTypetrue扩展支持的架构范围。 设置为 true 时,XsdDataContractImporter 将生成实现 IXmlSerializable 接口的类型。 这样就可以直接访问这些类型的 XML 表示形式。

设计注意事项
  • 直接处理弱类型 XML 表示形式可能会很困难。 可以考虑使用其他序列化引擎(例如 XmlSerializer),以便以强类型方式使用与数据协定不兼容的架构。 有关详细信息,请参阅 使用 XmlSerializer 类

  • 即使属性XsdDataContractImporter设置为ImportXmlType,某些架构构造仍无法由true导入。 同样,请考虑对此类情况使用 XmlSerializer

  • ImportXmlType中描述了在truefalse时支持的确切架构构造。

  • 导入和导出时,生成的 IXmlSerializable 类型的架构不会保留保真度。 那就是说,从生成的类型导出架构并作为类导入时,无法返回原始架构。

可以将该 ImportXmlType 选项与前面所述的选项组合在一起 ReferencedTypes 。 对于必须作为 IXmlSerializable 实现生成的类型,使用 ReferencedTypes 该功能时会跳过结构检查。

ImportXmlType 选项对应于 Svcutil.exe 工具上的 /importXmlTypes 开关。

使用生成的 IXmlSerializable 类型

生成的 IXmlSerializable 类型包含一个名为“nodesField”的专用字段,该字段返回对象数组 XmlNode 。 反序列化此类类型的实例时,可以使用 XML 文档对象模型直接通过此字段访问 XML 数据。 序列化此类型的实例时,可以将此字段设置为所需的 XML 数据,并对其进行序列化。

这是通过IXmlSerializable 的实现来完成的。 在生成的 IXmlSerializable 类型中,ReadXml 实现调用了 ReadNodes 类的 XmlSerializableServices 方法。 该方法是一种帮助程序方法,用于将通过 XmlReader 提供的 XML 转换为 XmlNode 对象数组。 WriteXml 的实现则将 XmlNode 对象的数组转换为一系列 XmlWriter 调用,反其道而行。 这是使用 WriteNodes 该方法完成的。

可以在生成的 IXmlSerializable 类上运行架构导出过程。 如前所述,不会返回原始架构。 相反,你将获得“anyType”标准 XSD 类型,这是任何 XSD 类型的通配符。

这可以通过将 XmlSchemaProviderAttribute 属性应用于生成的 IXmlSerializable 类并指定调用 AddDefaultSchema 该方法以生成“anyType”类型的方法来实现。

注释

XmlSerializableServices 类型仅用于支持此特定功能。 不建议用于任何其他目的。

导入选项:高级选项

以下是高级导入选项:

另请参阅