Menggunakan Kontrak Pesan

Biasanya ketika membuat aplikasi Windows Communication Foundation (WCF), pengembang memperhatikan struktur data dan masalah serialisasi serta tidak perlu menyibukkan diri dengan struktur pesan di mana data dibawa. Untuk aplikasi ini, pembuatan kontrak data untuk parameter atau nilai yang dikembalikan bersifat simpel. (Untuk informasi selengkapnya, lihat Menentukan Transfer Data dalam Kontrak Layanan.)

Namun, terkadang kontrol penuh atas struktur pesan SOAP sama pentingnya dengan kontrol atas isinya. Hal ini terutama berlaku ketika interoperabilitas diperlukan atau untuk secara khusus mengontrol masalah keamanan pada tingkat pesan atau bagian pesan. Dalam kasus ini, Anda dapat membuat kontrak pesan yang memungkinkan Anda menentukan struktur pesan SOAP persis yang diperlukan.

Topik ini membahas cara menggunakan berbagai atribut kontrak pesan untuk membuat kontrak pesan tertentu untuk operasi Anda.

Menggunakan Kontrak Pesan dalam Operasi

WCF mendukung operasi yang dimodelkan pada gaya panggilan prosedur jarak jauh (RPC) atau gaya olahpesan. Dalam operasi bergaya RPC, Anda dapat menggunakan jenis yang dapat diserialisasikan, dan Anda memiliki akses ke fitur yang tersedia untuk panggilan lokal, seperti beberapa parameter serta parameter ref dan out. Dalam gaya ini, bentuk serialisasi yang dipilih mengontrol struktur data dalam pesan yang mendasarinya, dan runtime WCF membuat pesan untuk mendukung operasi. Hal ini memungkinkan pengembang yang belum familier dengan SOAP dan pesan SOAP untuk dengan cepat dan mudah membuat dan menggunakan aplikasi layanan.

Contoh kode berikut menunjukkan operasi layanan yang dimodelkan pada gaya RPC.

[OperationContract]  
public BankingTransactionResponse PostBankingTransaction(BankingTransaction bt);  

Biasanya, kontrak data sudah cukup untuk menentukan skema untuk pesan. Misalnya, dalam contoh sebelumnya, cukup untuk sebagian besar aplikasi jika BankingTransaction dan BankingTransactionResponse memiliki kontrak data untuk menentukan konten pesan SOAP yang mendasarinya. Untuk informasi selengkapnya tentang kontrak data, lihat Menggunakan Kontrak Data.

Namun, terkadang perlu untuk mengontrol dengan tepat bagaimana struktur pesan SOAP ditransmisikan melalui kabel. Skenario yang paling umum untuk ini adalah memasukkan header SOAP kustom. Skenario umum lainnya adalah menentukan properti keamanan untuk header dan badan pesan, yaitu, untuk memutuskan apakah elemen-elemen ini ditandatangani dan dienkripsi secara digital. Terakhir, beberapa tumpukan SOAP pihak ketiga memerlukan pesan dalam format tertentu. Operasi bergaya olahpesan menyediakan kontrol ini.

Operasi bergaya olahpesan memiliki maksimal satu parameter dan satu nilai pengembalian di mana kedua jenis tersebut adalah jenis pesan, yaitu diserialisasikan langsung ke dalam struktur pesan SOAP tertentu. Ini mungkin berupa jenis apa pun yang ditandai dengan jenis MessageContractAttribute atau Message. Contoh kode berikut menunjukkan operasi yang mirip dengan gaya RCP sebelumnya, tetapi menggunakan gaya olahpesan.

Misalnya, jika BankingTransaction dan BankingTransactionResponse adalah jenis yang merupakan kontrak pesan, maka kode dalam operasi berikut valid.

[OperationContract]  
BankingTransactionResponse Process(BankingTransaction bt);  
[OperationContract]  
void Store(BankingTransaction bt);  
[OperationContract]  
BankingTransactionResponse GetResponse();  

Namun, kode berikut tidak valid.

[OperationContract]  
bool Validate(BankingTransaction bt);  
// Invalid, the return type is not a message contract.  
[OperationContract]  
void Reconcile(BankingTransaction bt1, BankingTransaction bt2);  
// Invalid, there is more than one parameter.  

Pengecualian dilemparkan untuk operasi apa pun yang melibatkan jenis kontrak pesan dan itu tidak mengikuti salah satu pola yang valid. Tentu saja, operasi yang tidak melibatkan jenis kontrak pesan tidak tunduk pada pembatasan ini.

Jika suatu jenis memiliki kontrak pesan dan kontrak data, hanya kontrak pesannya yang dipertimbangkan ketika jenis tersebut digunakan dalam operasi.

Menentukan Kontrak Pesan

Untuk menentukan kontrak pesan dari jenis (yaitu, untuk menentukan pemetaan antara jenis dan amplop SOAP), terapkan MessageContractAttribute ke jenis. Lalu, terapkan MessageHeaderAttribute ke anggota jenis yang ingin Anda jadikan header SOAP, dan terapkan MessageBodyMemberAttribute ke anggota yang ingin Anda sertakan dalam bagian isi SOAP pesan.

Kode berikut memberikan contoh penggunaan kontrak pesan.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageHeader] public DateTime transactionDate;  
  [MessageBodyMember] private Account sourceAccount;  
  [MessageBodyMember] private Account targetAccount;  
  [MessageBodyMember] public int amount;  
}  

Saat menggunakan jenis ini sebagai parameter operasi, amplop SOAP berikut dihasilkan:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
  <s:Header>  
    <h:operation xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">Deposit</h:operation>  
    <h:transactionDate xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">2012-02-16T16:10:00</h:transactionDate>  
  </s:Header>  
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <BankingTransaction xmlns="http://tempuri.org/">  
      <amount>0</amount>  
      <sourceAccount xsi:nil="true"/>  
      <targetAccount xsi:nil="true"/>  
    </BankingTransaction>  
  </s:Body>  
</s:Envelope>  

Perhatikan bahwa operation dan transactionDate muncul sebagai header SOAP dan isi SOAP terdiri dari elemen BankingTransaction pembungkus yang berisi sourceAccount,targetAccount, dan amount.

Anda dapat menerapkan MessageHeaderAttribute dan MessageBodyMemberAttribute ke semua bidang, properti, dan peristiwa, terlepas dari apakah itu bersifat publik, privat, dilindungi, atau internal.

MessageContractAttribute memungkinkan Anda menentukan atribut WrapperName dan WrapperNamespace yang mengontrol nama elemen pembungkus dalam isi pesan SOAP. Secara default, nama jenis kontrak pesan digunakan oleh pembungkus dan namespace tempat kontrak pesan didefinisikan http://tempuri.org/ digunakan sebagai namespace default.

Catatan

KnownTypeAttribute atribut diabaikan dalam kontrak pesan. Jika KnownTypeAttribute diperlukan, letakkan pada operasi yang menggunakan kontrak pesan yang dimaksud.

Mengontrol Nama dan Namespace Header dan Bagian Isi

Dalam representasi SOAP dari kontrak pesan, setiap header dan bagian isi dipetakan ke elemen XML yang memiliki nama dan namespace.

Secara default, namespace layanan sama dengan namespace kontrak layanan tempat pesan berpartisipasi, dan nama ditentukan oleh nama anggota tempat atribut MessageHeaderAttribute atau MessageBodyMemberAttribute diterapkan.

Anda dapat mengubah default ini dengan memanipulasi MessageContractMemberAttribute.Name dan MessageContractMemberAttribute.Namespace (pada kelas induk atribut MessageHeaderAttribute dan MessageBodyMemberAttribute).

Pertimbangkan kelas dalam contoh kode berikut.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageHeader(Namespace="http://schemas.contoso.com/auditing/2005")] public bool IsAudited;  
  [MessageBodyMember(Name="transactionData")] public BankingTransactionData theData;  
}  

Dalam contoh ini, header IsAudited berada di namespace yang ditentukan dalam kode, dan bagian isi yang mewakili anggota theData diwakili oleh elemen XML dengan nama transactionData. Contoh berikut menunjukkan XML yang dihasilkan untuk kontrak pesan ini.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
  <s:Header>  
    <h:IsAudited xmlns:h="http://schemas.contoso.com/auditing/2005" xmlns="http://schemas.contoso.com/auditing/2005">false</h:IsAudited>  
    <h:operation xmlns:h="http://tempuri.org/" xmlns="http://tempuri.org/">Deposit</h:operation>  
  </s:Header>  
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
    <AuditedBankingTransaction xmlns="http://tempuri.org/">  
      <transactionData/>  
    </AuditedBankingTransaction>  
  </s:Body>  
</s:Envelope>  

Mengontrol Apakah Bagian Isi SOAP Dibungkus

Secara default, bagian isi SOAP diserialisasikan di dalam elemen yang dibungkus. Misalnya, kode berikut menunjukkan elemen pembungkus HelloGreetingMessage yang dihasilkan dari nama jenis MessageContractAttribute dalam kontrak pesan untuk pesan HelloGreetingMessage.

[MessageContract]
public class HelloGreetingMessage
{
  private string localGreeting;

  [MessageBodyMember(
    Name = "Salutations",
    Namespace = "http://www.examples.com"
  )]
  public string Greeting
  {
    get { return localGreeting; }
    set { localGreeting = value; }
  }
}

/*
 The following is the request message, edited for clarity.

  <s:Envelope>
    <s:Header>
      <!-- Note: Some header content has been removed for clarity.
      <a:Action>http://GreetingMessage/Action</a:Action>
      <a:To s:mustUnderstand="1"></a:To>
    </s:Header>
    <s:Body u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <HelloGreetingMessage xmlns="Microsoft.WCF.Documentation">
        <Salutations xmlns="http://www.examples.com">Hello.</Salutations>
      </HelloGreetingMessage>
    </s:Body>
 </s:Envelope>
 */
<MessageContract> _
Public Class HelloGreetingMessage
    Private localGreeting As String

    <MessageBodyMember(Name:="Salutations", Namespace:="http://www.examples.com")> _
    Public Property Greeting() As String
        Get
            Return localGreeting
        End Get
        Set(ByVal value As String)
            localGreeting = value
        End Set
    End Property
End Class

'  
'   The following is the request message, edited for clarity.
'    
'    <s:Envelope>
'      <s:Header>
'        <!-- Note: Some header content has been removed for clarity.
'        <a:Action>http://GreetingMessage/Action</a:Action> 
'        <a:To s:mustUnderstand="1"></a:To>
'      </s:Header>
'      <s:Body u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
'        <HelloGreetingMessage xmlns="Microsoft.WCF.Documentation">
'          <Salutations xmlns="http://www.examples.com">Hello.</Salutations>
'      </s:Body>
'   </s:Envelope>
'   

Untuk menekan elemen pembungkus, atur properti IsWrapped ke false. Untuk mengontrol nama dan namespace elemen pembungkus, gunakan properti WrapperName dan WrapperNamespace.

Catatan

Memiliki lebih dari satu bagian isi pesan dalam pesan yang tidak dibungkus tidak sesuai dengan WS-I Basic Profile 1.1 dan tidak disarankan saat merancang kontrak pesan baru. Namun, mungkin diperlukan lebih dari satu bagian isi pesan yang tidak dibungkus dalam skenario interoperabilitas tertentu. Jika Anda akan mengirimkan lebih dari satu bagian data dalam isi pesan, sebaiknya gunakan mode default (dibungkus). Memiliki lebih dari satu header pesan dalam pesan yang tidak dibungkus diizinkan.

Menggunakan Jenis Kustom Di Dalam Kontrak Pesan

Setiap header pesan dan bagian isi pesan diserialisasikan (diubah menjadi XML) menggunakan mesin serialisasi pilihan untuk kontrak layanan tempat pesan digunakan. Mesin serialisasi default, XmlFormatter, dapat menangani jenis apa pun yang memiliki kontrak data, baik secara eksplisit (dengan memiliki System.Runtime.Serialization.DataContractAttribute) atau secara implisit (dengan menjadi jenis primitif, memiliki System.SerializableAttribute, dan sebagainya). Untuk informasi selengkapnya, lihat Menggunakan Kontrak Data.

Dalam contoh sebelumnya, jenis Operation dan BankingTransactionData harus memiliki kontrak data, dan transactionDate dapat diserialisasikan karena DateTime bersifat primitif (dan memiliki kontrak data implisit).

Namun, Anda dapat beralih ke mesin serialisasi yang berbeda, XmlSerializer. Jika Anda membuat pengalih tersebut, Anda harus memastikan bahwa semua jenis yang digunakan untuk header pesan dan bagian isi dapat diserialisasikan menggunakan XmlSerializer.

Menggunakan Array Di Dalam Kontrak Pesan

Anda dapat menggunakan array elemen berulang dalam kontrak pesan dengan dua cara.

Pertama, menggunakan MessageHeaderAttribute atau MessageBodyMemberAttribute langsung pada array. Dalam hal ini, seluruh array diserialisasikan sebagai satu elemen (yaitu, satu header atau satu bagian isi) dengan beberapa elemen turunan. Pertimbangkan kelas dalam contoh berikut.

[MessageContract]  
public class BankingDepositLog  
{  
  [MessageHeader] public int numRecords;  
  [MessageHeader] public DepositRecord[] records;  
  [MessageHeader] public int branchID;  
}  

Ini menghasilkan header SOAP mirip dengan hal berikut.

<BankingDepositLog>  
<numRecords>3</numRecords>  
<records>  
  <DepositRecord>Record1</DepositRecord>  
  <DepositRecord>Record2</DepositRecord>  
  <DepositRecord>Record3</DepositRecord>  
</records>  
<branchID>20643</branchID>  
</BankingDepositLog>  

Alternatifnya adalah menggunakan MessageHeaderArrayAttribute. Dalam hal ini, setiap elemen array diserialisasikan secara independen dan sehingga setiap elemen array memiliki satu header, mirip dengan hal berikut.

<numRecords>3</numRecords>  
<records>Record1</records>  
<records>Record2</records>  
<records>Record3</records>  
<branchID>20643</branchID>  

Nama default untuk entri array adalah nama anggota tempat atribut MessageHeaderArrayAttribute diterapkan.

Atribut MessageHeaderArrayAttribute mewarisi dari MessageHeaderAttribute. Ini memiliki serangkaian fitur yang sama dengan atribut non-array, misalnya, Anda dapat mengatur urutan, nama, dan namespace untuk array header dengan cara yang sama seperti Anda mengaturnya untuk satu header. Saat Anda menggunakan properti Order pada array, properti tersebut berlaku untuk seluruh array.

Anda dapat menerapkan MessageHeaderArrayAttribute hanya ke array, bukan koleksi.

Menggunakan Array Byte dalam Kontrak Pesan

Array byte, ketika digunakan dengan atribut non-array (MessageBodyMemberAttribute dan MessageHeaderAttribute), tidak diperlakukan sebagai array, tetapi sebagai jenis primitif khusus yang direpresentasikan sebagai data yang dikodekan Base64 dalam XML yang dihasilkan.

Saat Anda menggunakan array byte dengan MessageHeaderArrayAttribute atribut array, hasilnya bergantung pada pembuat serialisasi yang digunakan. Dengan pembuat serialisasi default, array direpresentasikan sebagai entri individu untuk setiap byte. Namun, ketika XmlSerializer dipilih, (menggunakan XmlSerializerFormatAttribute pada kontrak layanan), array byte diperlakukan sebagai data Base64 terlepas dari apakah menggunakan atribut array atau non-array.

Menandatangani dan Mengenkripsi Bagian Pesan

Kontrak pesan dapat menunjukkan apakah header dan/atau isi pesan harus ditandatangani dan dienkripsi secara digital.

Hal ini dilakukan dengan mengatur properti MessageContractMemberAttribute.ProtectionLevel pada atribut MessageHeaderAttribute dan MessageBodyMemberAttribute. Properti adalah enumerasi jenis System.Net.Security.ProtectionLevel dan dapat diatur ke None (tanpa enkripsi atau tanda tangan), Sign (tanda tangan digital saja), atau EncryptAndSign (enkripsi dan tanda tangan digital). Default adalah EncryptAndSign.

Agar fitur keamanan ini berfungsi, Anda harus mengonfigurasi pengikatan dan perilakunya dengan benar. Jika Anda menggunakan fitur keamanan ini tanpa konfigurasi yang tepat (misalnya, mencoba menandatangani pesan tanpa memberikan kredensial Anda), pengecualian akan dilemparkan pada waktu validasi.

Untuk header pesan, tingkat perlindungan ditentukan secara individual untuk setiap header.

Untuk bagian isi pesan, tingkat perlindungan dapat dianggap sebagai "tingkat perlindungan minimum". Isi hanya memiliki satu tingkat perlindungan, terlepas dari jumlah bagian isinya. Tingkat perlindungan isi ditentukan oleh pengaturan properti ProtectionLevel tertinggi dari semua bagian isi. Namun, Anda harus mengatur tingkat perlindungan setiap bagian isi ke tingkat perlindungan minimum aktual yang diperlukan.

Pertimbangkan kelas dalam contoh kode berikut.

[MessageContract]  
public class PatientRecord  
{  
   [MessageHeader(ProtectionLevel=None)] public int recordID;  
   [MessageHeader(ProtectionLevel=Sign)] public string patientName;  
   [MessageHeader(ProtectionLevel=EncryptAndSign)] public string SSN;  
   [MessageBodyMember(ProtectionLevel=None)] public string comments;  
   [MessageBodyMember(ProtectionLevel=Sign)] public string diagnosis;  
   [MessageBodyMember(ProtectionLevel=EncryptAndSign)] public string medicalHistory;  
}  

Dalam contoh ini, header recordID tidak dilindungi, patientName adalah signed, dan SSN dienkripsi dan ditandatangani. Setidaknya satu bagian isi, medicalHistory, telah menerapkan EncryptAndSign, dan dengan demikian seluruh isi pesan dienkripsi dan ditandatangani, meskipun bagian isi komentar dan diagnosis menentukan tingkat perlindungan yang lebih rendah.

Tindakan SOAP

SOAP dan standar layanan Web terkait menentukan properti yang disebut Action yang dapat disediakan untuk setiap pesan SOAP yang dikirim. Properti OperationContractAttribute.Action dan OperationContractAttribute.ReplyAction operasi mengontrol nilai properti ini.

Atribut Header SOAP

Standar SOAP mendefinisikan atribut berikut yang mungkin ada pada header:

  • Actor/Role (Actor di SOAP 1.1, Role di SOAP 1.2)

  • MustUnderstand

  • Relay

Atribut Actor atau Role menentukan Pengidentifikasi Sumber Daya Seragam (URI) dari node yang dimaksudkan oleh header tertentu. Atribut MustUnderstand menentukan apakah node yang memproses header harus memahaminya. Atribut Relay menentukan apakah header akan disampaikan ke node hilir. WCF tidak melakukan pemrosesan atribut apa pun pada pesan masuk, kecuali untuk atribut MustUnderstand, seperti yang ditentukan di bagian "Penerapan Versi Kontrak Pesan" nanti dalam topik ini. Namun, ini memungkinkan Anda untuk membaca dan menulis atribut ini seperlunya, seperti dalam deskripsi berikut.

Saat mengirim pesan, atribut ini tidak dikeluarkan secara default. Anda dapat mengubah ini dengan dua cara. Pertama, Anda dapat secara statis mengatur atribut ke nilai yang diinginkan dengan mengubah properti MessageHeaderAttribute.Actor, MessageHeaderAttribute.MustUnderstand, dan MessageHeaderAttribute.Relay, seperti yang ditunjukkan dalam contoh kode berikut. (Perhatikan bahwa tidak ada properti Role; mengatur properti Actor mengeluarkan atribut Role jika Anda menggunakan SOAP 1.2).

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader(Actor="http://auditingservice.contoso.com", MustUnderstand=true)] public bool IsAudited;  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public BankingTransactionData theData;  
}  

Cara kedua untuk mengontrol atribut ini adalah melalui kode secara dinamis. Anda dapat mencapai ini dengan membungkus jenis header yang diinginkan dalam jenis MessageHeader<T> (pastikan untuk tidak menyamakan jenis ini dengan versi non-generik) dan dengan menggunakan jenis bersama dengan MessageHeaderAttribute. Kemudian, Anda dapat menggunakan properti pada MessageHeader<T> untuk mengatur atribut SOAP, seperti yang ditunjukkan dalam contoh kode berikut.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public MessageHeader<bool> IsAudited;  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public BankingTransactionData theData;  
}  
// application code:  
BankingTransaction bt = new BankingTransaction();  
bt.IsAudited = new MessageHeader<bool>();  
bt.IsAudited.Content = false; // Set IsAudited header value to "false"  
bt.IsAudited.Actor="http://auditingservice.contoso.com";  
bt.IsAudited.MustUnderstand=true;  

Jika Anda menggunakan mekanisme kontrol dinamis dan statis, pengaturan statis digunakan sebagai default, tetapi Anda dapat menggantinya nanti dengan menggunakan mekanisme dinamis, seperti yang ditunjukkan pada kode berikut.

[MessageHeader(MustUnderstand=true)] public MessageHeader<Person> documentApprover;  
// later on in the code:  
BankingTransaction bt = new BankingTransaction();  
bt.documentApprover = new MessageHeader<Person>();  
bt.documentApprover.MustUnderstand = false; // override the static default of 'true'  

Membuat header berulang dengan kontrol atribut dinamis diperbolehkan, seperti yang ditunjukkan pada kode berikut.

[MessageHeaderArray] public MessageHeader<Person> documentApprovers[];  

Di sisi penerimaan, membaca atribut SOAP ini hanya dapat dilakukan jika kelas MessageHeader<T> digunakan untuk header dalam jenis. Periksa properti Actor, Relay, atau MustUnderstand dari header jenis MessageHeader<T> untuk menemukan pengaturan atribut pada pesan yang diterima.

Ketika pesan diterima dan kemudian dikirim kembali, pengaturan atribut SOAP hanya melakukan komunikasi dua arah untuk header jenis MessageHeader<T>.

Urutan Bagian Isi SOAP

Dalam beberapa keadaan, Anda mungkin perlu mengontrol urutan bagian isi. Elemen isi diurutkan berdasarkan alfabet secara default, tetapi dapat dikontrol oleh properti MessageBodyMemberAttribute.Order. Properti ini memiliki semantik yang sama dengan properti DataMemberAttribute.Order, kecuali untuk perilaku dalam skenario pewarisan (dalam kontrak pesan, anggota isi jenis dasar tidak diurutkan sebelum anggota isi jenis turunan). Untuk informasi selengkapnya, lihat Urutan Anggota Data.

Dalam contoh berikut, amount biasanya akan didahulukan karena memiliki urutan alfabet awal. Namun, properti Order menempatkannya ke posisi ketiga.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember(Order=1)] public Account sourceAccount;  
  [MessageBodyMember(Order=2)] public Account targetAccount;  
  [MessageBodyMember(Order=3)] public int amount;  
}  

Penerapan Versi Kontrak Pesan

Terkadang, Anda mungkin perlu mengubah kontrak pesan. Misalnya, versi baru aplikasi Anda mungkin menambahkan header tambahan ke pesan. Kemudian, ketika mengirim dari versi baru ke versi yang lama, sistem harus menangani header tambahan, serta header yang hilang ketika menuju ke arah lain.

Aturan berikut ini berlaku untuk header penerapan versi:

  • WCF tidak objek ke header yang hilang—anggota terkait dibiarkan menggunakan nilai default mereka.

  • WCF juga mengabaikan header tambahan yang tak terduga. Satu pengecualian untuk aturan ini adalah jika header tambahan memiliki atribut MustUnderstand yang diatur ke true dalam pesan SOAP masuk—dalam hal ini, pengecualian dilemparkan karena header yang harus dipahami tidak dapat diproses.

Isi pesan memiliki aturan penerapan versi yang sama—baik bagian isi pesan yang hilang maupun tambahan diabaikan.

Pertimbangan Warisan

Jenis kontrak pesan dapat mewarisi dari jenis lain, selama jenis dasar juga memiliki kontrak pesan.

Saat membuat atau mengakses pesan menggunakan jenis kontrak pesan yang mewarisi dari jenis kontrak pesan lainnya, aturan berikut berlaku:

  • Semua header pesan dalam hierarki warisan dikumpulkan bersama untuk membentuk set lengkap header untuk pesan.

  • Semua bagian isi pesan dalam hierarki warisan dikumpulkan bersama untuk membentuk isi pesan lengkap. Bagian isi diurutkan sesuai dengan aturan pengurutan biasa (berdasarkan properti MessageBodyMemberAttribute.Order, lalu alfabet), tanpa relevansi dengan tempat mereka dalam hierarki warisan. Menggunakan warisan kontrak pesan di mana bagian isi pesan muncul pada berbagai tingkat pohon warisan sangat tidak dianjurkan. Jika kelas dasar dan kelas turunan menentukan header atau bagian isi dengan nama yang sama, anggota dari kelas paling dasar digunakan untuk menyimpan nilai header atau bagian isi tersebut.

Pertimbangkan kelas dalam contoh kode berikut.

[MessageContract]  
public class PersonRecord  
{  
  [MessageHeader(Name="ID")] public int personID;  
  [MessageBodyMember] public string patientName;  
}  
  
[MessageContract]  
public class PatientRecord : PersonRecord  
{  
  [MessageHeader(Name="ID")] public int patientID;  
  [MessageBodyMember] public string diagnosis;  
}  

Kelas PatientRecord menjelaskan pesan dengan satu header yang disebut ID. Header tersebut sesuai dengan personID dan bukan anggota patientID, karena anggota paling dasar dipilih. Dengan demikian, bidang patientID tidak berguna dalam kasus ini. Isi pesan berisi elemen diagnosis yang diikuti oleh elemen patientName, karena urut secara alfabet. Perhatikan bahwa contoh menunjukkan pola yang sangat tidak dianjurkan: baik kontrak pesan dasar dan turunan memiliki bagian isi pesan.

Pertimbangan WSDL

Saat membuat kontrak Bahasa Deskripsi Layanan Web (WSDL) dari layanan yang menggunakan kontrak pesan, penting untuk diingat bahwa tidak semua fitur kontrak pesan tercermin dalam WSDL yang dihasilkan. Pertimbangkan poin-poin berikut:

  • WSDL tidak dapat mengekspresikan konsep array header. Saat membuat pesan dengan array header menggunakan MessageHeaderArrayAttribute, WSDL yang dihasilkan hanya mencerminkan satu header, bukan array.

  • Dokumen WSDL yang dihasilkan mungkin tidak mencerminkan beberapa informasi tingkat perlindungan.

  • Jenis pesan yang dihasilkan dalam WSDL memiliki nama yang sama dengan nama kelas jenis kontrak pesan.

  • Saat menggunakan kontrak pesan yang sama dalam beberapa operasi, beberapa jenis pesan dibuat dalam dokumen WSDL. Namanya dibuat unik dengan menambahkan angka "2", "3", dan seterusnya, untuk penggunaan berikutnya. Saat mengimpor kembali WSDL, beberapa jenis kontrak pesan dibuat dan bersifat identik kecuali untuk namanya.

Pertimbangan Pengodean SOAP

WCF memungkinkan Anda untuk menggunakan gaya pengodean SOAP lama XML, tetapi penggunaannya tidak disarankan. Saat menggunakan gaya ini (dengan mengatur properti Use ke Encoded pada System.ServiceModel.XmlSerializerFormatAttribute yang diterapkan ke kontrak layanan), pertimbangan tambahan berikut berlaku:

  • Header pesan tidak didukung; ini berarti bahwa atribut MessageHeaderAttribute dan MessageHeaderArrayAttribute atribut array tidak kompatibel dengan pengodean SOAP.

  • Jika kontrak pesan tidak dibungkus, yaitu, jika properti IsWrapped diatur ke false, kontrak pesan hanya dapat memiliki satu bagian isi.

  • Nama elemen pembungkus untuk kontrak pesan permintaan harus sesuai dengan nama operasi. Gunakan properti WrapperName kontrak pesan untuk hal ini.

  • Nama elemen pembungkus untuk kontrak pesan respons harus sama dengan nama operasi yang diakhiri dengan 'Response'. Gunakan properti WrapperName kontrak pesan untuk hal ini.

  • Pengodean SOAP mempertahankan referensi objek. Sebagai contoh, perhatikan kode berikut.

    [MessageContract(WrapperName="updateChangeRecord")]  
    public class ChangeRecordRequest  
    {  
      [MessageBodyMember] Person changedBy;  
      [MessageBodyMember] Person changedFrom;  
      [MessageBodyMember] Person changedTo;  
    }  
    
    [MessageContract(WrapperName="updateChangeRecordResponse")]  
    public class ChangeRecordResponse  
    {  
      [MessageBodyMember] Person changedBy;  
      [MessageBodyMember] Person changedFrom;  
      [MessageBodyMember] Person changedTo;  
    }  
    
    // application code:  
    ChangeRecordRequest cr = new ChangeRecordRequest();  
    Person p = new Person("John Doe");  
    cr.changedBy=p;  
    cr.changedFrom=p;  
    cr.changedTo=p;  
    

Setelah menserialisasi pesan menggunakan pengodean SOAP, changedFrom dan changedTo tidak berisi salinan p-nya sendiri, melainkan menunjuk ke salinan di dalam elemen changedBy.

Pertimbangan Performa

Setiap header pesan dan bagian isi pesan diserialisasi secara independen dari yang lain. Oleh karena itu, namespace yang sama dapat dideklarasikan lagi untuk setiap header dan bagian isi. Untuk meningkatkan performa, terutama dalam hal ukuran pesan pada kawat, gabungkan beberapa header dan bagian isi menjadi satu header atau bagian isi. Misalnya, alih-alih kode berikut ini:

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public Account sourceAccount;  
  [MessageBodyMember] public Account targetAccount;  
  [MessageBodyMember] public int amount;  
}  

Gunakan kode ini.

[MessageContract]  
public class BankingTransaction  
{  
  [MessageHeader] public Operation operation;  
  [MessageBodyMember] public OperationDetails details;  
}  
  
[DataContract]  
public class OperationDetails  
{  
  [DataMember] public Account sourceAccount;  
  [DataMember] public Account targetAccount;  
  [DataMember] public int amount;  
}  

Kontrak Pesan dan Asinkron Berbasis Peristiwa

Panduan desain untuk status model asinkron berbasis peristiwa bahwa jika lebih dari satu nilai dikembalikan, satu nilai dikembalikan sebagai properti Result dan yang lain dikembalikan sebagai properti pada objek EventArgs. Salah satu hasilnya adalah bahwa jika klien mengimpor metadata menggunakan opsi perintah asinkron berbasis peristiwa dan operasi mengembalikan lebih dari satu nilai, objek EventArgs default mengembalikan satu nilai sebagai properti Result dan sisanya adalah properti objek EventArgs.

Jika Anda ingin menerima objek pesan sebagai properti Result dan memiliki nilai yang dikembalikan sebagai properti pada objek tersebut, gunakan opsi perintah /messageContract. Ini menghasilkan tanda tangan yang mengembalikan pesan respons sebagai properti Result pada objek EventArgs. Semua nilai pengembalian internal kemudian merupakan properti dari objek pesan respons.

Lihat juga