Mengimpor Skema untuk Menghasilkan Kelas
Untuk menghasilkan kelas dari skema yang dapat digunakan dengan Windows Communication Foundation (WCF), gunakan kelas XsdDataContractImporter. Topik ini menjelaskan proses dan variasinya.
Proses Impor
Proses impor skema dimulai dengan XmlSchemaSet dan menghasilkan CodeCompileUnit.
XmlSchemaSet
adalah bagian dari Schema Object Model (SOM) .NET Framework yang mewakili sekumpulan dokumen skema bahasa definisi Skema XML (XSD). Untuk membuat objek XmlSchemaSet
dari sekumpulan dokumen XSD, deserialisasi setiap dokumen ke dalam objek XmlSchema (menggunakan XmlSerializer) dan tambahkan objek ini ke yang baru XmlSchemaSet
.
CodeCompileUnit
adalah bagian dari Model Objek Dokumen Kode (CodeDOM) .NET Framework yang mewakili kode .NET Framework dengan cara abstrak. Untuk menghasilkan kode aktual dari CodeCompileUnit
, gunakan subkelas dari kelas CodeDomProvider, seperti kelas CSharpCodeProvider atau VBCodeProvider.
Untuk mengimpor skema
Buat instans XsdDataContractImporter.
Opsional. Berikan
CodeCompileUnit
di konstruktor. Jenis yang dihasilkan selama impor skema ditambahkan ke instansCodeCompileUnit
ini alih-alih dimulai denganCodeCompileUnit
kosong.Opsional. Panggil salah satu metode CanImport. Metode ini menentukan apakah skema yang diberikan adalah skema kontrak data yang valid dan dapat diimpor. Metode
CanImport
memiliki overload yang sama denganImport
(langkah berikutnya).Panggil salah satu metode
Import
yang overload, misalnya, metode Import(XmlSchemaSet).Overload paling sederhana mengambil
XmlSchemaSet
dan mengimpor semua jenis, termasuk jenis anonim, yang ditemukan dalam set skema tersebut. Overload lainnya memungkinkan Anda menentukan jenis XSD atau daftar jenis yang akan diimpor (dalam bentuk XmlQualifiedName atau koleksi objekXmlQualifiedName
). Dalam hal ini, hanya jenis yang ditentukan yang diimpor. Overload mengambil XmlSchemaElement yang mengimpor elemen tertentu dariXmlSchemaSet
, serta jenis terkaitnya (apakah itu anonim atau tidak). Overload ini mengembalikanXmlQualifiedName
, yang mewakili nama kontrak data dari jenis yang dihasilkan untuk elemen ini.Beberapa panggilan metode
Import
mengakibatkan beberapa item ditambahkan keCodeCompileUnit
yang sama. Jenis tidak dihasilkan ke dalamCodeCompileUnit
jika sudah ada di sana. PanggilImport
beberapa kali padaXsdDataContractImporter
yang sama alih-alih menggunakan beberapa objekXsdDataContractImporter
. Ini adalah cara yang disarankan untuk menghindari jenis duplikat yang dihasilkan.Catatan
Jika ada kegagalan selama impor,
CodeCompileUnit
akan berada dalam status yang tidak dapat diprediksi. MenggunakanCodeCompileUnit
yang dihasilkan dari impor yang gagal dapat membuat Anda rentan terhadap keamanan.Akses
CodeCompileUnit
melalui properti CodeCompileUnit.
Opsi Impor: Menyesuaikan Jenis yang Dihasilkan
Anda dapat mengatur properti Options dari XsdDataContractImporter ke instans kelas ImportOptions untuk mengontrol berbagai aspek proses impor. Sejumlah opsi secara langsung memengaruhi jenis yang dihasilkan.
Mengontrol Tingkat Akses (GenerateInternal atau tombol /internal)
Ini sesuai dengan tombol /internal pada Alat Utilitas Metadata ServiceModel (Svcutil.exe).
Biasanya, jenis publik dihasilkan dari skema, dengan bidang privat dan properti anggota data publik yang cocok. Untuk menghasilkan jenis internal, atur properti GenerateInternal ke true
.
Contoh berikut menunjukkan skema yang diubah menjadi kelas internal saat properti GenerateInternal diatur ke 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
Mengontrol Namespace (Namespace atau tombol /namespace)
Ini sesuai dengan tombol /namespace pada alat Svcutil.exe
.
Biasanya, jenis yang dihasilkan dari skema dihasilkan ke dalam namespace .NET Framework, dengan setiap namespace XSD yang sesuai dengan namespace .NET Framework tertentu sesuai dengan pemetaan yang dijelaskan dalam Referensi Skema Kontrak Data. Anda dapat menyesuaikan pemetaan ini dengan properti Namespaces ke Dictionary<TKey,TValue>. Jika namespace XSD yang diberikan ditemukan dalam kamus, namespace layanan .NET Framework yang cocok juga diambil dari kamus Anda.
Misalnya, perhatikan skema berikut.
<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
<xs:complexType name="Vehicle">
<!-- details omitted... -->
</xs:complexType>
</xs:schema>
Contoh berikut menggunakan properti Namespaces
untuk memetakan namespace http://schemas.contoso.com/carSchema
ke "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"))
Menambahkan SerializableAttribute (GenerateSerializable atau tombol /serializable)
Ini sesuai dengan tombol /serializable pada alat Svcutil.exe
.
Terkadang penting bagi jenis yang dihasilkan dari skema agar dapat digunakan dengan mesin serialisasi runtime .NET Framework. Ini berguna saat menggunakan jenis untuk akses jauh .NET Framework. Untuk mengaktifkan ini, Anda harus menerapkan atribut SerializableAttribute ke jenis yang dihasilkan selain atribut DataContractAttribute reguler. Atribut dihasilkan secara otomatis jika opsi impor GenerateSerializable
diatur ke true
.
Contoh berikut menunjukkan kelas Vehicle
yang dihasilkan dengan opsi impor GenerateSerializable
diatur ke 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
Menambahkan Dukungan Pengikatan Data (EnableDataBinding atau tombol /enableDataBinding)
Ini sesuai dengan tombol /enableDataBinding pada alat Svcutil.exe.
Terkadang, Anda mungkin ingin mengikat jenis yang dihasilkan dari skema ke komponen antarmuka pengguna grafis sehingga setiap pembaruan pada instans jenis ini akan secara otomatis memperbarui UI. XsdDataContractImporter
dapat menghasilkan jenis yang mengimplementasikan antarmuka INotifyPropertyChanged sewaktu-waktu sehingga setiap perubahan properti memicu peristiwa. Jika Anda membuat jenis untuk digunakan dengan lingkungan pemrograman UI klien yang mendukung antarmuka ini (seperti Windows Presentation Foundation (WPF)), atur properti EnableDataBinding ke true
untuk mengaktifkan fitur ini.
Contoh berikut menunjukkan kelas Vehicle
yang dihasilkan dengan EnableDataBinding diatur ke 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
Opsi Impor: Memilih Jenis Koleksi
Dua pola khusus dalam XML mewakili kumpulan item: daftar item dan asosiasi antara satu item dan item lainnya. Berikut ini adalah contoh daftar string.
<People>
<person>Alice</person>
<person>Bob</person>
<person>Charlie</person>
</People>
Berikut ini adalah contoh hubungan antara string dan bilangan bulat (city name
dan 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>
Catatan
Asosiasi apa pun juga dapat dianggap sebagai daftar. Misalnya, Anda dapat melihat asosiasi sebelumnya sebagai daftar objek city
kompleks yang kebetulan memiliki dua bidang (bidang string dan bidang bilangan bulat). Kedua pola memiliki representasi dalam Skema XSD. Tidak ada cara untuk membedakan antara daftar dan asosiasi, sehingga pola tersebut selalu diperlakukan sebagai daftar kecuali anotasi khusus yang khusus untuk WCF ada dalam skema. Anotasi menunjukkan bahwa pola tertentu mewakili asosiasi. Untuk mengetahui informasi selengkapnya, lihat Referensi Skema Kontrak Data.
Biasanya, daftar diimpor sebagai kontrak data koleksi yang berasal dari Daftar Generik atau sebagai array .NET Framework, tergantung pada apakah skema mengikuti pola penamaan standar untuk koleksi atau tidak. Ini dijelaskan secara lebih rinci dalam Jenis Koleksi dalam Kontrak Data. Asosiasi biasanya diimpor sebagai Dictionary<TKey,TValue> atau kontrak data koleksi yang berasal dari objek kamus. Misalnya, perhatikan skema berikut.
<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>
Ini akan diimpor sebagai berikut (bidang ditampilkan alih-alih properti untuk keterbacaan).
[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
Dimungkinkan untuk menyesuaikan jenis koleksi yang dihasilkan untuk pola skema tersebut. Misalnya, Anda mungkin ingin menghasilkan koleksi yang berasal dari BindingList<T> alih-alih kelas List<T> untuk mengikat jenis ke kotak daftar dan memperbaruinya secara otomatis saat konten koleksi berubah. Untuk melakukan ini, atur properti ReferencedCollectionTypes kelas ImportOptions ke daftar jenis koleksi yang akan digunakan (selanjutnya dikenal sebagai jenis yang dirujuk). Saat mengimpor koleksi apa pun, daftar jenis koleksi yang direferensikan ini dipindai dan koleksi yang paling cocok digunakan jika ditemukan. Asosiasi hanya dicocokkan dengan jenis yang mengimplementasikan antarmuka IDictionary generik atau nongenerik, sementara daftar dicocokkan dengan jenis koleksi yang didukung.
Misalnya, jika properti ReferencedCollectionTypes diatur ke BindingList<T>, jenis people
dalam contoh sebelumnya dihasilkan sebagai berikut.
[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits BindingList(Of String)
Generik tertutup dianggap sebagai kecocokan terbaik. Misalnya, jika jenis BindingList(Of Integer)
dan ArrayList diteruskan ke kumpulan jenis yang direferensikan, daftar bilangan bulat apa pun yang ditemukan dalam skema diimpor sebagai BindingList(Of Integer)
. Daftar lain, misalnya, List(Of String)
, diimpor sebagai ArrayList
.
Jika jenis yang mengimplementasikan antarmuka IDictionary
generik ditambahkan ke kumpulan jenis yang dirujuk, parameter jenisnya harus sepenuhnya terbuka atau tertutup penuh.
Duplikat tidak diizinkan. Misalnya, Anda tidak dapat menambahkan List(Of Integer)
dan Collection(Of Integer)
ke jenis yang dirujuk. Sehingga menentukan mana yang harus digunakan saat daftar bilangan bulat ditemukan dalam skema sulit dilakukan. Duplikat akan terdeteksi hanya jika ada jenis skema yang memperlihatkan masalah duplikat. Misalnya, jika skema yang diimpor tidak berisi daftar bilangan bulat, skema tersebut diizinkan untuk memiliki List(Of Integer)
dan Collection(Of Integer)
dalam koleksi jenis yang direferensikan, tetapi tidak akan memiliki pengaruh apa pun.
Mekanisme jenis koleksi yang dirujuk bekerja sama baiknya untuk koleksi jenis kompleks (termasuk koleksi dari koleksi lain), dan bukan hanya untuk koleksi primitif.
Properti ReferencedCollectionTypes
sesuai dengan tombol /collectionType pada alat SvcUtil.exe. Perhatikan bahwa untuk mereferensikan beberapa jenis koleksi, tombol /collectionType harus ditentukan beberapa kali. Jika jenisnya tidak ada di MsCorLib.dll, perakitannya juga harus dirujuk menggunakan tombol /reference.
Opsi Impor: Mereferensikan Jenis yang Ada
Terkadang, jenis dalam skema sesuai dengan jenis .NET Framework yang ada, dan tidak perlu menghasilkan jenis ini dari awal. (Bagian ini hanya berlaku untuk jenis nonkoleksi. Untuk jenis koleksi, lihat bagian sebelumnya.)
Misalnya, Anda mungkin memiliki jenis kontrak data "Orang" di seluruh perusahaan standar yang selalu ingin Anda gunakan saat mewakili seseorang. Setiap kali beberapa layanan menggunakan jenis ini, dan skemanya muncul dalam metadata layanan, Anda mungkin ingin menggunakan kembali jenis Person
yang ada saat mengimpor skema ini alih-alih menghasilkan yang baru untuk setiap layanan.
Untuk melakukan ini, teruskan daftar jenis .NET Framework yang ingin Anda gunakan kembali ke koleksi yang dikembalikan properti ReferencedTypes pada kelas ImportOptions. Jika salah satu dari jenis ini memiliki nama kontrak data dan namespace yang cocok dengan nama dan namespace dari jenis skema, perbandingan struktural dilakukan. Jika ditentukan bahwa jenis memiliki nama yang cocok dan struktur yang cocok, jenis .NET Framework yang ada digunakan kembali alih-alih menghasilkan yang baru. Jika hanya nama yang cocok tetapi bukan struktur, pengecualian akan diterapkan. Perhatikan bahwa tidak ada kelonggaran untuk pembuatan versi saat mereferensikan jenis (misalnya, menambahkan anggota data opsional baru). Struktur harus sama persis.
Diizinkan untuk menambahkan beberapa jenis dengan nama kontrak data dan namespace yang sama ke koleksi jenis yang direferensikan, selama tidak ada jenis skema yang diimpor dengan nama dan namespace tersebut. Ini memungkinkan Anda dengan mudah menambahkan semua jenis dalam perakitan ke koleksi tanpa khawatir tentang duplikat untuk jenis yang sebenarnya tidak terjadi dalam skema.
Properti ReferencedTypes
sesuai dengan tombol /reference dalam mode operasi alat Svcutil.exe tertentu.
Catatan
Saat menggunakan Svcutil.exe atau (dalam Visual Studio) alat Tambahkan Referensi Layanan, semua jenis di MsCorLib.dll direferensikan secara otomatis.
Opsi Impor: Mengimpor Skema Non-DataContract sebagai jenis IXmlSerializable
XsdDataContractImporter mendukung subset terbatas dari skema. Jika konstruksi skema yang tidak didukung ada (misalnya, atribut XML), upaya impor gagal dengan pengecualian. Namun, mengatur properti ImportXmlType untuk true
memperluas rentang skema yang didukung. Saat diatur ke true
, XsdDataContractImporter menghasilkan jenis yang mengimplementasikan antarmuka IXmlSerializable. Ini memungkinkan akses langsung ke representasi XML dari jenis ini.
Pertimbangan Desain
Mungkin sulit untuk bekerja dengan representasi XML yang diketik dengan lemah secara langsung. Pertimbangkan untuk menggunakan mesin serialisasi alternatif, seperti XmlSerializer, untuk bekerja dengan skema yang tidak kompatibel dengan kontrak data dengan cara yang diketik dengan kuat. Untuk informasi selengkapnya, lihat Menggunakan Kelas XmlSerializer.
Beberapa konstruksi skema tidak dapat diimpor oleh XsdDataContractImporter bahkan ketika properti ImportXmlType diatur ke
true
. Sekali lagi, pertimbangkan untuk menggunakan XmlSerializer untuk kasus tersebut.Konstruksi skema persis yang didukung saat ImportXmlType adalah
true
ataufalse
dijelaskan dalam Referensi Skema Kontrak Data.Skema untuk jenis IXmlSerializable yang dihasilkan tidak mempertahankan keakuratan saat diimpor dan diekspor. Artinya, mengekspor skema dari jenis yang dihasilkan dan mengimpor karena kelas tidak mengembalikan skema asli.
Dimungkinkan untuk menggabungkan opsi ImportXmlType dengan opsi ReferencedTypes yang dijelaskan sebelumnya. Untuk jenis yang harus dihasilkan sebagai implementasi IXmlSerializable, pemeriksaan struktural dilewati saat menggunakan fitur ReferencedTypes.
Opsi ImportXmlType ini sesuai dengan tombol /importXmlTypes pada alat Svcutil.exe.
Bekerja dengan Jenis IXmlSerializable yang Dihasilkan
Jenis IXmlSerializable
yang dihasilkan berisi bidang privat, bernama "nodesField," yang mengembalikan array objek XmlNode. Saat mendeserialisasi instans jenis tersebut, Anda dapat mengakses data XML secara langsung melalui bidang ini dengan menggunakan Model Objek Dokumen XML. Saat menserialisasikan instans jenis ini, Anda dapat mengatur bidang ini ke data XML yang diinginkan dan akan diserialisasikan.
Hal ini dicapai melalui implementasi IXmlSerializable
. Dalam jenis IXmlSerializable
yang dihasilkan, implementasi ReadXml memanggil metode ReadNodes kelas XmlSerializableServices. Metode ini adalah metode pembantu yang mengonversi XML yang disediakan melalui XmlReader ke array objek XmlNode. Implementasi WriteXml melakukan kebalikannya dan mengonversi array objek XmlNode
menjadi urutan panggilan XmlWriter. Ini dicapai menggunakan metode WriteNodes.
Dimungkinkan untuk menjalankan proses ekspor skema pada kelas IXmlSerializable
yang dihasilkan. Seperti yang dinyatakan sebelumnya, Anda tidak akan mendapatkan skema asli kembali. Sebagai gantinya, Anda akan mendapatkan jenis XSD standar "anyType", yang merupakan wildcard untuk jenis XSD apa pun.
Ini dicapai dengan menerapkan atribut XmlSchemaProviderAttribute ke kelas IXmlSerializable
yang dihasilkan dan menentukan metode yang memanggil metode AddDefaultSchema untuk menghasilkan jenis "anyType".
Catatan
Jenis XmlSerializableServices hanya ada untuk mendukung fitur khusus ini. Tidak disarankan untuk digunakan untuk tujuan lain.
Opsi Impor: Opsi Tingkat Lanjut
Berikut ini adalah opsi impor tingkat lanjut:
properti CodeProvider. Tentukan CodeDomProvider yang akan digunakan untuk menghasilkan kode untuk kelas yang dihasilkan. Mekanisme impor mencoba menghindari fitur yang tidak didukung CodeDomProvider. Jika CodeProvider tidak diatur, rangkaian lengkap fitur .NET Framework digunakan tanpa batasan.
properti DataContractSurrogate. Implementasi IDataContractSurrogate dapat ditentukan dengan properti ini. IDataContractSurrogate menyesuaikan proses impor. Untuk mengetahui informasi selengkapnya, lihat Pengganti Kontrak Data. Secara default, tidak ada pengganti yang digunakan.