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
Crie uma instância de XsdDataContractImporter.
Opcional. Passe um
CodeCompileUnit
no construtor. Os tipos gerados durante a importação de esquema são adicionados a estaCodeCompileUnit
instância em vez de começar com um arquivo em brancoCodeCompileUnit
.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 queImport
(a próxima etapa).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 deXmlQualifiedName
objetos). Nesse caso, apenas os tipos especificados são importados. Uma sobrecarga tira um XmlSchemaElement elemento que importa um determinado elemento doXmlSchemaSet
, bem como seu tipo associado (seja anônimo ou não). Essa sobrecarga retorna umXmlQualifiedName
arquivo , 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 mesmoCodeCompileUnit
. Um tipo não é gerado noCodeCompileUnit
se ele já existe lá. LigueImport
várias vezes no mesmoXsdDataContractImporter
em vez de usar váriosXsdDataContractImporter
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 umCodeCompileUnit
resultado de uma importação com falha pode expô-lo a vulnerabilidades de segurança.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 ArrayList
arquivo .
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
oufalse
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:
CodeProvider imóvel. Especifique o CodeDomProvider a ser usado para gerar o código para as classes geradas. O mecanismo de importação tenta evitar recursos que o CodeDomProvider não suporta. Se o CodeProvider não estiver definido, o conjunto completo de recursos do .NET Framework será usado sem restrições.
DataContractSurrogate imóvel. Uma IDataContractSurrogate implementação pode ser especificada com esta propriedade. O IDataContractSurrogate personaliza o processo de importação. Para obter mais informações, consulte Substitutos de contrato de dados. Por padrão, nenhum substituto é usado.