Импорт схемы для создания классов
Чтобы создать классы из схем, которые доступны для использования в Windows Communication Foundation (WCF), используйте XsdDataContractImporter этот класс. В данном разделе описывается процесс и параметры импорта.
Процесс импорта
Процесс импорта схемы начинается с создания объекта XmlSchemaSet, что приводит к созданию объекта CodeCompileUnit.
Это XmlSchemaSet
часть объектной модели схемы платформа .NET Framework (SOM), представляющая набор документов схемы XSD. Чтобы создать объект XmlSchemaSet
из набора документов XSD, десериализуйте каждый документ в объект XmlSchema (с помощью сериализатора XmlSerializer) и добавьте эти объекты в новый объект XmlSchemaSet
.
Это CodeCompileUnit
часть объектной модели кода платформа .NET Framework (CodeDOM), представляющая платформа .NET Framework код абстрактным образом. Чтобы создать фактический код из CodeCompileUnit
, используйте подкласс класса CodeDomProvider, например класс CSharpCodeProvider или класс VBCodeProvider.
Процедура импорта схемы
Создайте экземпляр XsdDataContractImporter.
Необязательно. Передайте объект
CodeCompileUnit
в конструктор. Типы, созданные во время импорта схемы, добавляются в этот экземпляр классаCodeCompileUnit
вместо создания нового пустого экземпляра классаCodeCompileUnit
.Необязательно. Вызовите один из методов CanImport. Метод определяет, является ли данная схема действительной схемой контракта данных и можно ли ее импортировать. Метод
CanImport
имеет те же перегрузки, что методImport
(см. следующий шаг).Вызовите один из перегруженных методов
Import
, например метод Import(XmlSchemaSet).Простейшая перегрузка принимает объект
XmlSchemaSet
и импортирует все типы, включая анонимные, найденные в данном наборе схем. Другие перегрузки позволяют указывать тип XSD или список типов для импорта (в виде объекта XmlQualifiedName или коллекции объектовXmlQualifiedName
). В этом случае импортируются только указанные типы. Перегрузка принимает объект XmlSchemaElement, импортирующий определенный элемент из объектаXmlSchemaSet
и его связанный тип (анонимный или нет). Эта перегрузка возвращает объектXmlQualifiedName
, представляющий имя контракта данных типа, созданного для этого элемента.При нескольких вызовах метода
Import
в один и тот же объектCodeCompileUnit
добавляется несколько типов. Тип не создается в объектеCodeCompileUnit
, если он уже имеется в нем. Вызовите методImport
несколько раз для одного и того же объектаXsdDataContractImporter
вместо использования нескольких объектовXsdDataContractImporter
. Этот метод рекомендуется использовать, чтобы избежать создания повторяющихся типов.Примечание.
В случае сбоя при импорте объект
CodeCompileUnit
будет находиться в непредсказуемом состоянии. Использование объектаCodeCompileUnit
, возникшего после сбоя импорта, может привести к образованию уязвимых мест в системе безопасности.Для доступа к объекту
CodeCompileUnit
используется свойство CodeCompileUnit .
Параметры импорта. Настройка созданных типов
Можно присвоить свойство Options объекта XsdDataContractImporter экземпляру класса ImportOptions для управления различными аспектами процесса импорта. Некоторые параметры непосредственно влияют на созданные типы.
Управление уровнем доступа (GenerateInternal или коммутатор /internal)
Это соответствует параметру /internal switch on the ServiceModel Metadata Utility Tool (Svcutil.exe).
Как правило, открытые типы создаются из схемы с закрытыми полями и соответствующими свойствами открытых членов данных. Чтобы вместо этого создать внутренние типы, присвойте свойству 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)
Это соответствует переключателю пространства имен /name в средстве 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)
Это соответствует переключателю /enableDataBinding в средстве Svcutil.exe.
Иногда возникает необходимость привязать созданные из схемы типы к компонентам графического пользовательского интерфейса, чтобы при каждом обновлении экземпляров этих типов автоматически обновлялся пользовательский интерфейс. Объект XsdDataContractImporter
может создавать типы, реализующие интерфейс INotifyPropertyChanged способом, обеспечивающим вызов события при любом изменении свойства. Если вы создаете типы для использования с средой программирования пользовательского интерфейса клиента, которая поддерживает этот интерфейс (например, Windows Presentation Foundation (WPF)), задайте 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 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<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
, добавляется в коллекцию ссылочных типов, параметры этого типа должны быть либо полностью открытыми, либо полностью закрытыми.
Дубликаты не разрешены. Например, нельзя добавить и 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. CodeProvider Если параметр не задан, полный набор функций платформа .NET Framework используется без ограничений.
Свойство DataContractSurrogate. С помощью этого свойства можно указать реализацию IDataContractSurrogate. В реализации IDataContractSurrogate настраивается процесс импорта. Дополнительные сведения см. в статье "Суррогаты контракта данных". По умолчанию суррогат не используется.