Импорт схемы для создания классов

Чтобы создать классы из схем, которые доступны для использования в 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.

Процедура импорта схемы

  1. Создайте экземпляр XsdDataContractImporter.

  2. Необязательно. Передайте объект CodeCompileUnit в конструктор. Типы, созданные во время импорта схемы, добавляются в этот экземпляр класса CodeCompileUnit вместо создания нового пустого экземпляра класса CodeCompileUnit.

  3. Необязательно. Вызовите один из методов CanImport. Метод определяет, является ли данная схема действительной схемой контракта данных и можно ли ее импортировать. Метод CanImport имеет те же перегрузки, что метод Import (см. следующий шаг).

  4. Вызовите один из перегруженных методов Import, например метод Import(XmlSchemaSet).

    Простейшая перегрузка принимает объект XmlSchemaSet и импортирует все типы, включая анонимные, найденные в данном наборе схем. Другие перегрузки позволяют указывать тип XSD или список типов для импорта (в виде объекта XmlQualifiedName или коллекции объектов XmlQualifiedName). В этом случае импортируются только указанные типы. Перегрузка принимает объект XmlSchemaElement, импортирующий определенный элемент из объекта XmlSchemaSet и его связанный тип (анонимный или нет). Эта перегрузка возвращает объект XmlQualifiedName, представляющий имя контракта данных типа, созданного для этого элемента.

    При нескольких вызовах метода Import в один и тот же объект CodeCompileUnit добавляется несколько типов. Тип не создается в объекте CodeCompileUnit , если он уже имеется в нем. Вызовите метод Import несколько раз для одного и того же объекта XsdDataContractImporter вместо использования нескольких объектов XsdDataContractImporter. Этот метод рекомендуется использовать, чтобы избежать создания повторяющихся типов.

    Примечание.

    В случае сбоя при импорте объект CodeCompileUnit будет находиться в непредсказуемом состоянии. Использование объекта CodeCompileUnit, возникшего после сбоя импорта, может привести к образованию уязвимых мест в системе безопасности.

  5. Для доступа к объекту 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.

  • Точные конструкции схемы, которые поддерживаются при ImportXmlTypetrue использовании или 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 настраивается процесс импорта. Дополнительные сведения см. в статье "Суррогаты контракта данных". По умолчанию суррогат не используется.

См. также