Импорт схемы для создания классов
Чтобы создать классы из схем, которые могут использоваться в Windows Communication Foundation (WCF), используйте класс XsdDataContractImporter. В данном разделе описывается процесс и параметры импорта.
Процесс импорта
Процесс импорта схемы начинается с создания объекта XmlSchemaSet, что приводит к созданию объекта CodeCompileUnit.
Класс XmlSchemaSet является частью объектной модели схемы (SOM) платформы .NET Framework, представляющей набор документов схемы на языке определения схемы XML (XSD). Чтобы создать объект XmlSchemaSet из набора документов XSD, десериализуйте каждый документ в объект XmlSchema (с помощью сериализатора XmlSerializer) и добавьте эти объекты в новый объект XmlSchemaSet.
Класс CodeCompileUnit является частью документной объектной модели кода (CodeDOM) платформы .NET Framework, представляющей код .NET Framework абстрактным способом. Чтобы создать фактический код из CodeCompileUnit, используйте подкласс класса CodeDomProvider, например класс CSharpCodeProvider или класс VBCodeProvider.
Процедура импорта схемы
Создайте экземпляр класса XsdDataContractImporter.
Необязательный. Передайте объект CodeCompileUnit в конструктор. Типы, созданные во время импорта схемы, добавляются в этот экземпляр класса CodeCompileUnit вместо создания нового пустого экземпляра класса CodeCompileUnit.
Необязательный. Вызовите один из методов CanImport. Метод определяет, является ли данная схема действительной схемой контракта данных и можно ли ее импортировать. Метод CanImport имеет те же перегрузки, что метод Import (см. следующий шаг).
Вызовите один из перегруженных методов Import, например метод Import.
Простейшая перегрузка принимает объект XmlSchemaSet и импортирует все типы, включая анонимные, найденные в данном наборе схем. Другие перегрузки позволяют указывать тип XSD или список типов для импорта (в виде объекта XmlQualifiedName или коллекции объектов XmlQualifiedName). В этом случае импортируются только указанные типы. Перегрузка принимает объект XmlSchemaElement, импортирующий определенный элемент из объекта XmlSchemaSet и его связанный тип (анонимный или нет). Эта перегрузка возвращает объект XmlQualifiedName, представляющий имя контракта данных типа, созданного для этого элемента.
При нескольких вызовах метода Import в один и тот же объект CodeCompileUnit добавляется несколько типов. Тип не создается в объекте CodeCompileUnit, если он уже имеется в нем. Вызовите метод Import несколько раз для одного и того же объекта XsdDataContractImporter вместо использования нескольких объектов XsdDataContractImporter. Этот метод рекомендуется использовать, чтобы избежать создания повторяющихся типов.
Примечание В случае сбоя при импорте объект CodeCompileUnit будет находиться в непредсказуемом состоянии. Использование объекта CodeCompileUnit, возникшего после сбоя импорта, может привести к образованию уязвимых мест в системе безопасности. Для доступа к объекту CodeCompileUnit используется свойство CodeCompileUnit.
Параметры импорта. Настройка созданных типов
Можно присвоить свойство Options объекта XsdDataContractImporter экземпляру класса ImportOptions для управления различными аспектами процесса импорта. Некоторые параметры непосредственно влияют на созданные типы.
Управление уровнем доступа (GenerateInternal или коммутатор /internal)
Соответствует коммутатору /internal в средстве Служебное средство ServiceModel Metadata Utility Tool (Svcutil.exe).
Как правило, открытые типы создаются из схемы с закрытыми полями и соответствующими свойствами открытых членов данных. Чтобы вместо этого создать внутренние типы, присвойте свойству GenerateInternal значение true.
В следующем примере показана схема, преобразованная во внутренний класс после присвоения свойству GenerateInternal значения true.
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
[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;}
}
}
Управление пространствами имен (Namespaces или коммутатор /namespace)
Соответствует коммутатору /namespace в средстве Svcutil.exe.
Как правило, созданные из схемы типы, создаются в пространствах имен .NET Framework, при этом каждое пространство имен XSD соответствует определенному пространству имен .NET Framework в соответствии с сопоставлением, описанным в разделе Справочник по схеме контрактов данных. Это сопоставление можно настроить, присвоив свойству Namespaces значение Dictionary. Если пространство имен 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".
Namespace Contoso.Cars
Class Vehicle
Implements IExtensibleDataObject
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
End Namespace
namespace Contoso.Cars {
[DataContract]
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.");
}
}
}
Добавление SerializableAttribute (GenerateSerializable или коммутатор /serializable)
Соответствует коммутатору /serializable в средстве Svcutil.exe.
Иногда важно, чтобы созданные из схемы типы могли использоваться в модулях сериализации среды выполнения .NET Framework (например, классе BinaryFormatter и классе SoapFormatter). Это полезно при использовании типов для удаленного взаимодействия .NET Framework. Чтобы включить эту возможность, необходимо применить атрибут SerializableAttribute к созданным типам помимо обычного атрибута DataContractAttribute. Этот атрибут создается автоматически, если параметр импорта GenerateSerializable имеет значение true.
В следующем примере показан класс Vehicle
, при создании которого параметр импорта GenerateSerializable был установлен на значение true.
<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
[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.");
}
}
}
Добавление поддержки привязки данных (EnableDataBinding или коммутатор /enableDataBinding)
Соответствует коммутатору /enableDataBinding в средстве Svcutil.exe.
Иногда возникает необходимость привязать созданные из схемы типы к компонентам графического пользовательского интерфейса, чтобы при каждом обновлении экземпляров этих типов автоматически обновлялся пользовательский интерфейс. Объект XsdDataContractImporter может создавать типы, реализующие интерфейс INotifyPropertyChanged способом, обеспечивающим вызов события при любом изменении свойства. При создании типов для использования в среде программирования пользовательского интерфейса клиента, поддерживающей этот интерфейс (например, Windows Presentation Foundation (WPF)), присвойте свойству EnableDataBinding значение true, чтобы включить эту функцию.
В следующем примере показан класс Vehicle
, при создании которого параметр импорта EnableDataBinding был установлен на значение true.
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
[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;}
}
}
Параметры импорта. Выбор типов коллекции
Коллекции элементов представляют два специальных шаблона в формате XML: список элементов и ассоциации между двумя элементами. Ниже представлен пример списка строк.
<People>
<person>Alice</person>
<person>Bob</person>
<person>Charlie</person>
</People>
Ниже приведен пример ассоциации между строкой и целым числом (city name
и 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>
Примечание |
---|
Любая ассоциация может также считаться списком. Например, указанную выше ассоциацию можно рассматривать как список сложных объектов city с двумя полями (полем строки и целочисленным полем). Оба шаблона имеют представление в схеме XSD. Список и ассоциацию невозможно различить, поэтому такие шаблоны всегда обрабатываются как списки, если в схеме отсутствует специальная заметка, характерная для WCF. В заметке указывается, что данный шаблон представляет ассоциацию. Дополнительные сведения см. в разделе Справочник по схеме контрактов данных.
|
Как правило, список импортируется как контракт данных коллекции, наследуемый от универсального списка, или как массив .NET Framework в зависимости от того, используется ли в схеме стандартный шаблон именования коллекций. Более подробное описание см. в разделе Типы коллекций в контрактах данных. Обычно ассоциации импортируются либо в виде Dictionary, либо в виде контракта данных коллекции, наследуемого от объекта словаря. Например, рассмотрим следующую схему.
<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>
Импорт выполняется следующим образом (вместо свойств для удобочитаемости показаны поля.)
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
[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> {}
Можно настроить типы коллекций, созданные для таких шаблонов схемы. Например, может потребоваться создать коллекции, наследуемые от класса BindingList, а не от класса List, для привязки типа к списку и его автоматического обновления при изменении содержимого коллекции. Для этого присвойте свойство ReferencedCollectionTypes класса ImportOptions списку типов коллекций, которые должны использоваться (в дальнейшем называемых "ссылочные типы"). При импорте какой-либо коллекции выполняется сканирование этого списка ссылочных типов коллекций и используется наиболее подходящая коллекция, если таковая найдена. Ассоциации сопоставляются только с типами, реализующими универсальный или неуниверсальный интерфейс IDictionary, в то время как списки сопоставляются с любым поддерживаемым типом коллекции.
Например, если свойству ReferencedCollectionTypes присвоено значение BindingList, тип people
в предыдущем примере создается следующим образом.
<CollectionDataContract(ItemName := "person")> _
Public Class people
Inherits BindingList(Of String)
[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
Закрытый универсальный интерфейс считается наиболее подходящим. Например, если типы BindingList(Of Integer) и ArrayList передаются в коллекцию ссылочных типов, любые списки целых чисел, найденные в схеме, импортируются как BindingList(Of Integer). Любые другие списки, например List(Of String), импортируются как ArrayList.
Если тип, реализующий универсальный интерфейс IDictionary, добавляется в коллекцию ссылочных типов, параметры этого типа должны быть либо полностью открытыми, либо полностью закрытыми.
Дубликаты не разрешены. Например, нельзя добавить и List(Of Integer), и Collection(Of Integer) в ссылочные типы. Это делает невозможным определение типа, который следует использовать при обнаружении списка целых чисел в схеме. Дубликаты обнаруживаются, только если в схеме есть тип, указывающий на проблему дубликатов. Например, если импортируемая схема не содержит список целых чисел, допускается иметь оба типа: List(Of Integer) и Collection(Of Integer) в коллекции ссылочных типов, но ни тот, ни другой не будут действовать.
Механизм ссылочных типов коллекций работает одинаково хорошо как для коллекций сложных типов (включая коллекции других коллекций), так и для коллекций примитивов.
Свойство ReferencedCollectionTypes соответствует коммутатору /collectionType в средстве SvcUtil.exe. Обратите внимание, чтобы задать ссылку на несколько типов коллекций, коммутатор /collectionType следует указать несколько раз. Если тип отсутствует в библиотеке MsCorLib.dll, также необходимо указать ссылку на его сборку с помощью коммутатора /reference.
Параметры импорта. Ссылка на существующие типы
Иногда типы в схеме соответствуют существующим типам .NET Framework, и нет необходимости создавать эти типы с нуля. (Сведения, представленные в данном разделе, применимы только к типам, не являющимся коллекциями. Дополнительные сведения о типах коллекций см. в предыдущем разделе.)
Например, имеется стандартный корпоративный тип контракта данных "Person", который всегда необходимо использовать при представлении лица. Всякий раз при использовании этого типа некоторыми службами и при отображении его схемы в метаданных службы может возникать необходимость в повторном использовании существующего типа Person
при импорте этой схемы вместо создания нового типа для каждой службы.
Для этого передайте список типов .NET Framework для повторного использования в коллекцию, возвращаемую свойством ReferencedTypes в класс ImportOptions. Если какой-либо из этих типов имеет имя контракта данных и пространство имен, соответствующие имени и пространству имен типа схемы, выполняется структурное сравнение. Если обнаружено, что типы имеют как соответствующие имена, так и соответствующие структуры, вместо создания нового типа существующий тип .NET Framework используется повторно. Если соответствует только имя, но не структура, вызывается исключение. Обратите внимание, при создании ссылок на типы управление версиями не допускается (например, добавление новых необязательных членов данных). Структуры должны совпадать полностью.
Допустимо добавлять несколько типов с одинаковым именем контракта данных и пространством имен в коллекцию ссылочных типов, если никакие типы схемы не импортируются с этим именем и пространством имен. Это позволит быстро добавлять все типы в сборке в коллекцию, не задумываясь о возможных проблемах с дубликатами для типов, которые в настоящий момент отсутствуют в схеме.
Свойство ReferencedTypes соответствует коммутатору /reference в определенных режимах работы средства Svcutil.exe.
Примечание |
---|
При использовании средства Svcutil.exe или (в Visual Studio) средства Добавить ссылку на службу, ссылки на все типы в библиотеке MsCorLib.dll создаются автоматически. |
Параметры импорта. Импорт схемы, отличной от DataContract, в виде типов IXmlSerializable
Объект XsdDataContractImporter поддерживает ограниченное подмножество схемы. При наличии неподдерживаемых конструкций схемы (например, атрибутов XML) попытка импорта заканчивается с ошибкой, и возникает исключение. Однако, если присвоить свойству ImportXmlType значение true, диапазон поддерживаемых схем увеличится. При выборе значения true объект XsdDataContractImporter создает типы, реализующие интерфейс IXmlSerializable. Это обеспечивает прямой доступ к XML-представлению этих типов.
Вопросы проектирования
Возможно, будет трудно работать со слабо типизированным XML-представлением напрямую. При работе со схемой, не совместимой с контрактами данных строго типизированным способом, рекомендуется использовать альтернативный модуль сериализации, такой как XmlSerializer. Дополнительные сведения см. в разделе Использование класса XmlSerializer.
Некоторые конструкции схемы невозможно импортировать с помощью XsdDataContractImporter, даже если свойству ImportXmlType присвоено значение true. В таких случаях также рекомендуется использовать XmlSerializer.
Точные конструкции схемы, поддерживаемые, если свойствуImportXmlType присвоено значение true или false, описаны в разделе Справочник по схеме контрактов данных.
При импорте и экспорте точность схемы для созданных типов IXmlSerializable не сохраняется. Это значит, что при экспорте схемы из созданных типов и импорте в виде классов исходная схема не возвращается.
Можно сгруппировать параметр ImportXmlType и параметр ReferencedTypes, описанные выше. В случае типов, которые необходимо создать как реализации IXmlSerializable, структурная проверка пропускается при использовании функции ReferencedTypes.
Параметр ImportXmlType соответствует коммутатору /importXmlTypes в средстве Svcutil.exe.
Работа с созданными типами IXmlSerializable
Созданные типы IXmlSerializable содержат закрытое поле с именем "nodesField", возвращающее массив объектов XmlNode. При десериализации экземпляра такого типа доступ к данным XML можно получить непосредственно через это поле с помощью документной объектной модели XML. При сериализации экземпляра этого типа можно настроить это поле на требуемые данные XML, и он будет сериализован.
Это возможно благодаря реализации IXmlSerializable. В созданном типе IXmlSerializable реализация ReadXml вызывает метод ReadNodes класса XmlSerializableServices. Этот метод является вспомогательным методом, преобразующим данные XML, предоставленные с помощью XmlReader, в массив объектов XmlNode. Реализация WriteXml выполняет противоположные действия и преобразовывает массив объектов XmlNode в последовательность вызовов XmlWriter. Это возможно благодаря методу WriteNodes.
Процесс экспорта схемы можно выполнить в созданных классах IXmlSerializable. Как уже говорилось ранее, исходная схема не возвращается. Вместо этого возвращается стандартный тип XSD "anyType", который является подстановочным знаком для любого типа XSD.
Это возможно благодаря применению атрибута XmlSchemaProviderAttribute к созданным классам IXmlSerializable и указанию метода, вызывающего метод AddDefaultSchema для создания типа "anyType".
Примечание |
---|
Тип XmlSerializableServices существует только для поддержки этой конкретной функции. Не рекомендуется использовать его для других целей. |
Параметры импорта. Дополнительные параметры
Ниже представлены дополнительные параметры импорта.
Свойство CodeProvider. Укажите класс CodeDomProvider, используемый для создания кода для созданных классов. Механизм импорта пытается избежать функций, не поддерживаемых классом CodeDomProvider. Например, в языке J# не поддерживаются универсальные шаблоны. Если указать поставщика кода J# в этом свойстве, универсальные типы в классе CodeCompileUnit импортера создаваться не будут. Если для свойства CodeProvider значение не задано, используется полный набор функций .NET Framework без ограничений.
Свойство DataContractSurrogate. С помощью этого свойства можно указать реализацию IDataContractSurrogate. В реализации IDataContractSurrogate настраивается процесс импорта. Дополнительные сведения см. в разделе Суррогаты контрактов данных. По умолчанию суррогат не используется.
См. также
Справочник
DataContractSerializer
XsdDataContractImporter
XsdDataContractExporter
ImportOptions
Основные понятия
Справочник по схеме контрактов данных
Суррогаты контрактов данных
Импорт и экспорт схемы
Экспорт схем из классов
Справочник по схеме контрактов данных