Partilhar via


Importando esquema para gerar classes

Para gerar classes de esquemas que são utilizáveis com o Windows Communication Foundation (WCF), use a XsdDataContractImporter classe. Este tópico descreve o processo e as variações.

O Processo de Importação

O processo de importação de esquema começa com um XmlSchemaSet e produz um CodeCompileUnitarquivo .

O XmlSchemaSet é uma parte do Schema Object Model (SOM) do .NET Framework que representa um conjunto de documentos de esquema XSD (XML Schema Definition Language). Para criar um XmlSchemaSet objeto a partir de um conjunto de documentos XSD, desserialize cada documento em um XmlSchema objeto (usando o XmlSerializer) e adicione esses objetos a um novo XmlSchemaSet.

O CodeCompileUnit é parte do Code Document Object Model (CodeDOM) do .NET Framework que representa o código do .NET Framework de forma abstrata. Para gerar o código real de um CodeCompileUnit, use uma subclasse da CodeDomProvider classe, como a CSharpCodeProvider classe or VBCodeProvider .

Para importar um esquema

  1. Crie uma instância de XsdDataContractImporter.

  2. Opcional. Passe um CodeCompileUnit no construtor. Os tipos gerados durante a importação de esquema são adicionados a esta CodeCompileUnit instância em vez de começar com um arquivo em branco CodeCompileUnit.

  3. Opcional. Chame um dos CanImport métodos. O método determina se o esquema fornecido é um esquema de contrato de dados válido e pode ser importado. O CanImport método tem as mesmas sobrecargas que Import (a próxima etapa).

  4. Chame um dos métodos sobrecarregados Import , por exemplo, o Import(XmlSchemaSet) método.

    A sobrecarga mais simples usa e XmlSchemaSet importa todos os tipos, incluindo tipos anônimos, encontrados nesse conjunto de esquemas. Outras sobrecargas permitem especificar o tipo XSD ou uma lista de tipos a importar (na forma de um XmlQualifiedName ou uma coleção de XmlQualifiedName objetos). Nesse caso, apenas os tipos especificados são importados. Uma sobrecarga tira um XmlSchemaElement elemento que importa um determinado elemento do XmlSchemaSet, bem como seu tipo associado (seja anônimo ou não). Essa sobrecarga retorna um XmlQualifiedNamearquivo , que representa o nome do contrato de dados do tipo gerado para esse elemento.

    Várias chamadas do Import método resultam em vários itens sendo adicionados ao mesmo CodeCompileUnit. Um tipo não é gerado no CodeCompileUnit se ele já existe lá. Ligue Import várias vezes no mesmo XsdDataContractImporter em vez de usar vários XsdDataContractImporter objetos. Esta é a maneira recomendada de evitar que tipos duplicados sejam gerados.

    Nota

    Se houver uma falha durante a importação, o CodeCompileUnit estará em um estado imprevisível. O uso de um CodeCompileUnit resultado de uma importação com falha pode expô-lo a vulnerabilidades de segurança.

  5. Aceda através CodeCompileUnit da CodeCompileUnit propriedade.

Opções de importação: personalizando os tipos gerados

Você pode definir a Options propriedade do XsdDataContractImporter como uma instância da ImportOptions classe para controlar vários aspetos do processo de importação. Várias opções influenciam diretamente os tipos gerados.

Controlando o Nível de Acesso (GenerateInternal ou o switch /internal)

Isso corresponde à opção /internal na ServiceModel Metadata Utility Tool (Svcutil.exe).

Normalmente, os tipos públicos são gerados a partir do esquema, com campos privados e propriedades de membro de dados públicos correspondentes. Para gerar tipos internos, defina a GenerateInternal propriedade como true.

O exemplo a seguir mostra um esquema transformado em uma classe interna quando a GenerateInternal propriedade é definida como 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

Controlando namespaces (namespaces ou a opção /namespace)

Isso corresponde à opção /namespace na Svcutil.exe ferramenta.

Normalmente, os tipos gerados a partir do esquema são gerados em namespaces do .NET Framework, com cada namespace XSD correspondendo a um namespace específico do .NET Framework de acordo com um mapeamento descrito em Referência de esquema de contrato de dados. Você pode personalizar esse mapeamento pela Namespaces propriedade para um Dictionary<TKey,TValue>arquivo . Se um determinado namespace XSD for encontrado no dicionário, o namespace .NET Framework correspondente também será retirado do dicionário.

Por exemplo, considere o esquema a seguir.

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

O exemplo a seguir usa a Namespaces propriedade para mapear o http://schemas.contoso.com/carSchema namespace para "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"))

Adicionando o SerializableAttribute (GenerateSerializable ou a opção /serializable)

Isso corresponde à opção /serializable na Svcutil.exe ferramenta.

Às vezes, é importante que os tipos gerados a partir do esquema sejam utilizáveis com mecanismos de serialização de tempo de execução do .NET Framework. Isso é útil ao usar tipos para comunicação remota do .NET Framework. Para habilitar isso, você deve aplicar o SerializableAttribute atributo aos tipos gerados, além do atributo regular DataContractAttribute . O atributo é gerado automaticamente se a GenerateSerializable opção de importação estiver definida como true.

O exemplo a seguir mostra a Vehicle classe gerada com a GenerateSerializable opção import definida como 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

Adicionando suporte à vinculação de dados (EnableDataBinding ou a opção /enableDataBinding)

Isso corresponde à opção /enableDataBinding na ferramenta Svcutil.exe.

Às vezes, você pode querer vincular os tipos gerados a partir do esquema aos componentes da interface gráfica do usuário para que qualquer atualização para instâncias desses tipos atualize automaticamente a interface do usuário. O XsdDataContractImporter pode gerar tipos que implementam a INotifyPropertyChanged interface de tal forma que qualquer alteração de propriedade dispara um evento. Se você estiver gerando tipos para uso com um ambiente de programação de interface do usuário cliente que ofereça suporte a essa interface (como o Windows Presentation Foundation (WPF)), defina a EnableDataBinding propriedade para true habilitar esse recurso.

O exemplo a seguir mostra a Vehicle classe gerada com o EnableDataBinding conjunto como 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

Opções de importação: escolhendo tipos de coleção

Dois padrões especiais em XML representam coleções de itens: listas de itens e associações entre um item e outro. A seguir está um exemplo de uma lista de cadeias de caracteres.

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

A seguir está um exemplo de uma associação entre uma cadeia de caracteres e um inteiro (city name e population).

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

Nota

Qualquer associação também pode ser considerada uma lista. Por exemplo, você pode exibir a associação anterior como uma lista de objetos complexos city que têm dois campos (um campo de cadeia de caracteres e um campo inteiro). Ambos os padrões têm uma representação no esquema XSD. Não há como diferenciar entre uma lista e uma associação, portanto, esses padrões são sempre tratados como listas, a menos que uma anotação especial específica para WCF esteja presente no esquema. A anotação indica que um determinado padrão representa uma associação. Para obter mais informações, consulte Referência de esquema de contrato de dados.

Normalmente, uma lista é importada como um contrato de dados de coleção que deriva de uma Lista Genérica ou como uma matriz do .NET Framework, dependendo se o esquema segue ou não o padrão de nomenclatura padrão para coleções. Isso é descrito com mais detalhes em Tipos de coleta em contratos de dados. As associações são normalmente importadas como um Dictionary<TKey,TValue> contrato de dados de coleta ou um contrato de dados de coleta que deriva do objeto de dicionário. Por exemplo, considere o esquema a seguir.

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

Isso seria importado da seguinte forma (campos são mostrados em vez de propriedades para legibilidade).

[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

É possível personalizar os tipos de coleção que são gerados para esses padrões de esquema. Por exemplo, talvez você queira gerar coleções derivadas da BindingList<T> classe em vez da classe para vincular o tipo a uma caixa de List<T> listagem e fazer com que ela seja atualizada automaticamente quando o conteúdo da coleção for alterado. Para fazer isso, defina a ReferencedCollectionTypesImportOptions propriedade da classe como uma lista de tipos de coleção a serem usados (doravante conhecidos como os tipos referenciados). Ao importar qualquer coleção, essa lista de tipos de coleção referenciados é verificada e a coleção de melhor correspondência é usada se for encontrada. As associações são correspondidas apenas com tipos que implementam a interface genérica ou não genérica IDictionary , enquanto as listas são comparadas com qualquer tipo de coleção suportado.

Por exemplo, se a ReferencedCollectionTypes propriedade for definida como , BindingList<T>o people tipo no exemplo anterior será gerado da seguinte maneira.

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

Um genérico fechado é considerado a melhor combinação. Por exemplo, se os tipos BindingList(Of Integer) e ArrayList forem passados para a coleção de tipos referenciados, todas as listas de inteiros encontradas no esquema serão importadas como um BindingList(Of Integer)arquivo . Quaisquer outras listas, por exemplo, a List(Of String), são importadas como um ArrayListarquivo .

Se um tipo que implementa a interface genérica IDictionary é adicionado à coleção de tipos referenciados, seus parâmetros de tipo devem ser totalmente abertos ou totalmente fechados.

Não são permitidos duplicados. Por exemplo, não é possível adicionar a List(Of Integer) e a Collection(Of Integer) aos tipos referenciados. Isso tornaria impossível determinar qual deve ser usado quando uma lista de inteiros é encontrada no esquema. As duplicatas serão detetadas somente se houver um tipo no esquema que exponha o problema de duplicatas. Por exemplo, se o esquema importado não contiver listas de inteiros, é permitido ter a List(Of Integer) coleção de tipos referenciados e a Collection(Of Integer) na mesma, mas nenhum deles terá qualquer efeito.

O mecanismo de tipos de coleção referenciados funciona igualmente bem para coleções de tipos complexos (incluindo coleções de outras coleções), e não apenas para coleções de primitivos.

A ReferencedCollectionTypes propriedade corresponde à opção /collectionType na ferramenta SvcUtil.exe. Observe que, para fazer referência a vários tipos de coleção, a opção /collectionType deve ser especificada várias vezes. Se o tipo não estiver no MsCorLib.dll, seu conjunto também deve ser referenciado usando a opção /reference .

Opções de importação: fazendo referência a tipos existentes

Ocasionalmente, os tipos no esquema correspondem aos tipos existentes do .NET Framework e não há necessidade de gerar esses tipos do zero. (Esta secção aplica-se apenas a tipos que não sejam de recolha. Para tipos de coleção, consulte a seção anterior.)

Por exemplo, você pode ter um tipo de contrato de dados "Pessoa" padrão em toda a empresa que você sempre deseja usar ao representar uma pessoa. Sempre que algum serviço fizer uso desse tipo e seu esquema aparecer nos metadados do serviço, convém reutilizar o tipo existente Person ao importar esse esquema em vez de gerar um novo para cada serviço.

Para fazer isso, passe uma lista de tipos do .NET Framework que você deseja reutilizar na coleção que a ReferencedTypes propriedade retorna na ImportOptions classe. Se qualquer um desses tipos tiver um nome de contrato de dados e namespace que corresponda ao nome e namespace de um tipo de esquema, uma comparação estrutural será executada. Se for determinado que os tipos têm nomes e estruturas correspondentes, o tipo .NET Framework existente é reutilizado em vez de gerar um novo. Se apenas o nome corresponder, mas não a estrutura, uma exceção será lançada. Observe que não há permissão para controle de versão ao referenciar tipos (por exemplo, adicionar novos membros de dados opcionais). As estruturas devem corresponder exatamente.

É legal adicionar vários tipos com o mesmo nome de contrato de dados e namespace à coleção de tipos referenciados, desde que nenhum tipo de esquema seja importado com esse nome e namespace. Isso permite que você adicione facilmente todos os tipos em um assembly à coleção sem se preocupar com duplicatas para tipos que realmente não ocorrem no esquema.

A ReferencedTypes propriedade corresponde ao interruptor /reference em certos modos de operação da ferramenta Svcutil.exe.

Nota

Ao usar o Svcutil.exe ou (no Visual Studio) as ferramentas Adicionar Referência de Serviço , todos os tipos em MsCorLib.dll são referenciados automaticamente.

Opções de importação: Importando esquema não-DataContract como tipos IXmlSerializable

O XsdDataContractImporter suporta um subconjunto limitado do esquema. Se construções de esquema sem suporte estiverem presentes (por exemplo, atributos XML), a tentativa de importação falhará com uma exceção. No entanto, definir a ImportXmlType propriedade para true estende o intervalo de esquema suportado. Quando definido como true, o XsdDataContractImporter gera tipos que implementam a IXmlSerializable interface. Isso permite o acesso direto à representação XML desses tipos.

Considerações de design
  • Pode ser difícil trabalhar diretamente com a representação XML fracamente tipada. Considere o uso de um mecanismo de serialização alternativo, como o XmlSerializer, para trabalhar com esquemas não compatíveis com contratos de dados de forma fortemente tipada. Para obter mais informações, consulte Usando a classe XmlSerializer.

  • Algumas construções de esquema não podem ser importadas XsdDataContractImporter pelo mesmo quando a ImportXmlType propriedade está definida como true. Novamente, considere usar o XmlSerializer para esses casos.

  • As construções de esquema exatas que são suportadas quando ImportXmlType é true ou false são descritas em Referência de esquema de contrato de dados.

  • O esquema para tipos gerados IXmlSerializable não mantém fidelidade quando importado e exportado. Ou seja, exportar o esquema dos tipos gerados e importar como classes não retorna o esquema original.

É possível combinar a ImportXmlType opção com a ReferencedTypes opção anteriormente descrita. Para tipos que precisam ser gerados como IXmlSerializable implementações, a verificação estrutural é ignorada ao usar o ReferencedTypes recurso.

A ImportXmlType opção corresponde à opção /importXmlTypes na ferramenta Svcutil.exe.

Trabalhando com tipos IXmlSerializable gerados

Os tipos gerados IXmlSerializable contêm um campo privado, chamado "nodesField", que retorna uma matriz de XmlNode objetos. Ao desserializar uma instância desse tipo, você pode acessar os dados XML diretamente por meio desse campo usando o Modelo de Objeto de Documento XML. Ao serializar uma instância desse tipo, você pode definir esse campo para os dados XML desejados e ele será serializado.

Isto é conseguido através da IXmlSerializable implementação. No tipo gerado IXmlSerializable , a ReadXml implementação chama o ReadNodesXmlSerializableServices método da classe. O método é um método auxiliar que converte XML fornecido através de um XmlReader para uma matriz de XmlNode objetos. A WriteXml implementação faz o oposto e converte a matriz de XmlNode objetos em uma sequência de XmlWriter chamadas. Isso é feito usando o WriteNodes método.

É possível executar o processo de exportação de esquema nas classes geradas IXmlSerializable . Como dito anteriormente, você não receberá o esquema original de volta. Em vez disso, você obterá o tipo XSD padrão "anyType", que é um curinga para qualquer tipo XSD.

Isso é feito aplicando o XmlSchemaProviderAttribute atributo às classes geradas IXmlSerializable e especificando um método que chama o AddDefaultSchema método para gerar o tipo "anyType".

Nota

O XmlSerializableServices tipo existe apenas para suportar este recurso específico. Não é recomendado para uso para qualquer outra finalidade.

Opções de importação: Opções avançadas

A seguir estão as opções avançadas de importação:

Consulte também