Använda meddelandekontrakt
När utvecklare skapar WCF-program (Windows Communication Foundation) är de vanligtvis noga med datastrukturerna och serialiseringsproblemen och behöver inte bry sig om strukturen för de meddelanden som data transporteras i. För dessa program är det enkelt att skapa datakontrakt för parametrarna eller returvärdena. (Mer information finns i Ange dataöverföring i tjänstkontrakt.)
Men ibland är fullständig kontroll över strukturen för ett SOAP-meddelande lika viktig som kontroll över dess innehåll. Detta gäller särskilt när samverkan är viktig eller för att specifikt kontrollera säkerhetsproblem på meddelande- eller meddelandedelens nivå. I dessa fall kan du skapa ett meddelandekontrakt som gör att du kan ange strukturen för det exakta SOAP-meddelande som krävs.
I det här avsnittet beskrivs hur du använder de olika attributen för meddelandekontrakt för att skapa ett specifikt meddelandekontrakt för din åtgärd.
Använda meddelandekontrakt i åtgärder
WCF stöder åtgärder som är modellerade på RPC-format (remote procedure call) eller meddelandeformatet. I en RPC-stil kan du använda valfri serialiserbar typ och du har åtkomst till de funktioner som är tillgängliga för lokala anrop, till exempel flera parametrar och ref
out
parametrar. I det här formatet styr den typ av serialisering som valts strukturen för data i de underliggande meddelandena, och WCF-körningen skapar meddelandena som stöd för åtgärden. Detta gör det möjligt för utvecklare som inte är bekanta med SOAP- och SOAP-meddelanden att snabbt och enkelt skapa och använda tjänstprogram.
I följande kodexempel visas en tjänståtgärd som modelleras enligt RPC-formatet.
[OperationContract]
public BankingTransactionResponse PostBankingTransaction(BankingTransaction bt);
Normalt räcker det med ett datakontrakt för att definiera schemat för meddelandena. I föregående exempel räcker det till exempel för de flesta program om BankingTransaction
och BankingTransactionResponse
har datakontrakt för att definiera innehållet i de underliggande SOAP-meddelandena. Mer information om datakontrakt finns i Använda datakontrakt.
Ibland är det dock nödvändigt att exakt kontrollera hur soap-meddelandets struktur överförs över kabeln. Det vanligaste scenariot för detta är att infoga anpassade SOAP-huvuden. Ett annat vanligt scenario är att definiera säkerhetsegenskaper för meddelandets sidhuvuden och brödtext, dvs. för att avgöra om dessa element är digitalt signerade och krypterade. Slutligen kräver vissa SOAP-stackar från tredje part att meddelanden är i ett visst format. Åtgärder i meddelandeformat ger den här kontrollen.
En meddelandeliknande åtgärd har högst en parameter och ett returvärde där båda typerna är meddelandetyper. De serialiseras alltså direkt till en angiven SOAP-meddelandestruktur. Det kan vara vilken typ som helst som har markerats Message med MessageContractAttribute typen eller . I följande kodexempel visas en åtgärd som liknar den föregående RCP-stilen, men som använder meddelandeformatet.
Om och BankingTransactionResponse
till exempel BankingTransaction
båda typerna är meddelandekontrakt är koden i följande åtgärder giltig.
[OperationContract]
BankingTransactionResponse Process(BankingTransaction bt);
[OperationContract]
void Store(BankingTransaction bt);
[OperationContract]
BankingTransactionResponse GetResponse();
Följande kod är dock ogiltig.
[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.
Ett undantag utlöses för alla åtgärder som omfattar en meddelandekontraktstyp och som inte följer något av de giltiga mönstren. Åtgärder som inte omfattar meddelandekontraktstyper omfattas naturligtvis inte av dessa begränsningar.
Om en typ har både ett meddelandekontrakt och ett datakontrakt beaktas endast dess meddelandekontrakt när typen används i en åtgärd.
Definiera meddelandekontrakt
Om du vill definiera ett meddelandekontrakt för en typ (d.v.s. definiera mappningen mellan typen och ett SOAP-kuvert) tillämpar du på MessageContractAttribute typen. Tillämpa MessageHeaderAttribute sedan på de medlemmar av den typ som du vill göra i SOAP-huvuden och tillämpa på MessageBodyMemberAttribute de medlemmar som du vill göra i delar av SOAP-brödtexten i meddelandet.
Följande kod innehåller ett exempel på hur du använder ett meddelandekontrakt.
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageHeader] public DateTime transactionDate;
[MessageBodyMember] private Account sourceAccount;
[MessageBodyMember] private Account targetAccount;
[MessageBodyMember] public int amount;
}
När du använder den här typen som en åtgärdsparameter genereras följande SOAP-kuvert:
<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>
Observera att operation
och transactionDate
visas som SOAP-huvuden och SOAP-brödtexten består av ett wrapper-element BankingTransaction
som innehåller sourceAccount
, targetAccount
och amount
.
Du kan tillämpa och MessageBodyMemberAttribute på MessageHeaderAttribute alla fält, egenskaper och händelser, oavsett om de är offentliga, privata, skyddade eller interna.
Med MessageContractAttribute kan du ange attributen WrapperName och WrapperNamespace som styr namnet på omslutningselementet i brödtexten i SOAP-meddelandet. Som standard används namnet på meddelandekontraktstypen för omslutningen och namnområdet där meddelandekontraktet definieras http://tempuri.org/
används som standardnamnområde.
Kommentar
KnownTypeAttribute attribut ignoreras i meddelandekontrakt. Om en KnownTypeAttribute krävs placerar du den på den åtgärd som använder meddelandekontraktet i fråga.
Kontrollera rubrik- och brödtextdelsnamn och namnområden
I SOAP-representationen av ett meddelandekontrakt mappar varje rubrik och brödtextdel till ett XML-element som har ett namn och ett namnområde.
Som standard är namnområdet samma som namnområdet för tjänstkontraktet som meddelandet deltar i, och namnet bestäms av medlemsnamnet som attributen MessageHeaderAttribute MessageBodyMemberAttribute eller tillämpas på.
Du kan ändra dessa standardvärden genom att MessageContractMemberAttribute.Name ändra och MessageContractMemberAttribute.Namespace (på den överordnade klassen för attributen MessageHeaderAttribute och MessageBodyMemberAttribute ).
Överväg klassen i följande kodexempel.
[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;
}
I det här exemplet IsAudited
finns rubriken i det namnområde som anges i koden och brödtextdelen som representerar theData
medlemmen representeras av ett XML-element med namnet transactionData
. Följande visar DEN XML som genererats för det här meddelandekontraktet.
<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>
Kontrollera om SOAP-brödtextdelarna är omslutna
Som standard serialiseras SOAP-brödtextdelarna i ett omslutet element. Följande kod visar HelloGreetingMessage
till exempel omslutningselementet som genererats från namnet på MessageContractAttribute typen i meddelandekontraktet HelloGreetingMessage
för meddelandet.
[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>
'
Om du vill utelämna omslutningselementet anger du IsWrapped egenskapen till false
. Om du vill styra namnet och namnområdet för omslutningselementet använder du WrapperName egenskaperna och WrapperNamespace .
Kommentar
Att ha mer än en meddelandetextdel i meddelanden som inte är omslutna är inte kompatibelt med WS-I Basic Profile 1.1 och rekommenderas inte när du utformar nya meddelandekontrakt. Det kan dock vara nödvändigt att ha mer än en obearbetad meddelandetextdel i vissa specifika samverkansscenarier. Om du ska överföra mer än en del data i en meddelandetext rekommenderar vi att du använder standardläget (omslutet). Det är helt acceptabelt att ha fler än ett meddelandehuvud i oöppnade meddelanden.
Använda anpassade typer i meddelandekontrakt
Varje enskilt meddelandehuvud och meddelandetextdel serialiseras (omvandlas till XML) med den valda serialiseringsmotorn för tjänstkontraktet där meddelandet används. Standard serialiseringsmotorn , XmlFormatter
kan hantera alla typer som har ett datakontrakt, antingen explicit (genom att ha System.Runtime.Serialization.DataContractAttribute) eller implicit (genom att vara en primitiv typ, med System.SerializableAttribute, och så vidare). Mer information finns i Använda datakontrakt.
I föregående exempel måste typerna Operation
och BankingTransactionData
ha ett datakontrakt och transactionDate
är serialiserbara eftersom DateTime det är en primitiv (och därför har ett implicit datakontrakt).
Det är dock möjligt att växla till en annan serialiseringsmotor, XmlSerializer
. Om du gör en sådan växel bör du se till att alla typer som används för meddelandehuvuden och brödtextdelar är serialiserbara med hjälp XmlSerializer
av .
Använda matriser i meddelandekontrakt
Du kan använda matriser med upprepade element i meddelandekontrakt på två sätt.
Den första är att använda en MessageHeaderAttribute eller en MessageBodyMemberAttribute direkt i matrisen. I det här fallet serialiseras hela matrisen som ett element (dvs. en rubrik eller en brödtextdel) med flera underordnade element. Överväg klassen i följande exempel.
[MessageContract]
public class BankingDepositLog
{
[MessageHeader] public int numRecords;
[MessageHeader] public DepositRecord[] records;
[MessageHeader] public int branchID;
}
Detta resulterar i SOAP-huvuden liknar följande.
<BankingDepositLog>
<numRecords>3</numRecords>
<records>
<DepositRecord>Record1</DepositRecord>
<DepositRecord>Record2</DepositRecord>
<DepositRecord>Record3</DepositRecord>
</records>
<branchID>20643</branchID>
</BankingDepositLog>
Ett alternativ till detta är att använda MessageHeaderArrayAttribute. I det här fallet serialiseras varje matriselement oberoende av varandra och så att varje matriselement har en rubrik, ungefär som följande.
<numRecords>3</numRecords>
<records>Record1</records>
<records>Record2</records>
<records>Record3</records>
<branchID>20643</branchID>
Standardnamnet för matrisposter är namnet på den medlem som attributen MessageHeaderArrayAttribute tillämpas på.
Attributet MessageHeaderArrayAttribute ärver från MessageHeaderAttribute. Den har samma uppsättning funktioner som icke-matrisattribut, till exempel är det möjligt att ange ordning, namn och namnrymd för en matris med rubriker på samma sätt som du anger den för en enda rubrik. När du använder egenskapen Order
i en matris gäller den för hela matrisen.
Du kan endast använda matriser MessageHeaderArrayAttribute , inte samlingar.
Använda bytematriser i meddelandekontrakt
Byte-matriser, när de används med icke-matrisattributen (MessageBodyMemberAttribute och MessageHeaderAttribute), behandlas inte som matriser utan som en särskild primitiv typ som representeras som Base64-kodade data i den resulterande XML-koden.
När du använder bytematriser med matrisattributet MessageHeaderArrayAttributeberor resultatet på den serialiserare som används. Med standard-serialiseraren representeras matrisen som en enskild post för varje byte. Men när XmlSerializer
har valts (med hjälp av XmlSerializerFormatAttribute i tjänstkontraktet) behandlas bytematriser som Base64-data oavsett om matrisen eller icke-matrisattribut används.
Signera och kryptera delar av meddelandet
Ett meddelandekontrakt kan ange om rubrikerna och/eller brödtexten i meddelandet ska signeras och krypteras digitalt.
Detta görs genom att ange MessageContractMemberAttribute.ProtectionLevel egenskapen för attributen MessageHeaderAttribute och MessageBodyMemberAttribute . Egenskapen är en uppräkning av System.Net.Security.ProtectionLevel typen och kan anges till None (ingen kryptering eller signatur), Sign (endast digital signatur) eller EncryptAndSign (både kryptering och en digital signatur). Standardvärdet är EncryptAndSign.
För att dessa säkerhetsfunktioner ska fungera måste du konfigurera bindningen och beteendet korrekt. Om du använder dessa säkerhetsfunktioner utan rätt konfiguration (till exempel försök att signera ett meddelande utan att ange dina autentiseringsuppgifter) genereras ett undantag vid valideringstillfället.
För meddelandehuvuden bestäms skyddsnivån individuellt för varje rubrik.
För meddelandetextdelar kan skyddsnivån betraktas som "lägsta skyddsnivå". Kroppen har bara en skyddsnivå, oavsett antalet kroppsdelar. Brödtextens skyddsnivå bestäms av den högsta ProtectionLevel egenskapsinställningen för alla kroppsdelar. Du bör dock ange skyddsnivån för varje kroppsdel till den faktiska lägsta skyddsnivå som krävs.
Överväg klassen i följande kodexempel.
[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;
}
I det här exemplet recordID
är rubriken inte skyddad, patientName
är signed
, och SSN
är krypterad och signerad. Minst en brödtextdel, medicalHistory
, har EncryptAndSign tillämpats och därmed krypteras och signeras hela meddelandetexten, även om kommentarerna och diagnostiseringskroppsdelarna anger lägre skyddsnivåer.
SOAP-åtgärd
SOAP och relaterade webbtjänststandarder definierar en egenskap med namnet Action
som kan finnas för varje SOAP-meddelande som skickas. Åtgärdens OperationContractAttribute.Action och OperationContractAttribute.ReplyAction egenskaperna styr värdet för den här egenskapen.
SOAP-rubrikattribut
SOAP-standarden definierar följande attribut som kan finnas i en rubrik:
Actor/Role
(Actor
i SOAP 1.1,Role
i SOAP 1.2)MustUnderstand
Relay
Attributet Actor
eller Role
anger URI (Uniform Resource Identifier) för noden som ett visst huvud är avsett för. Attributet MustUnderstand
anger om noden som bearbetar huvudet måste förstå det. Attributet Relay
anger om rubriken ska vidarebefordras till underordnade noder. WCF utför inte någon bearbetning av dessa attribut på inkommande meddelanden, förutom MustUnderstand
attributet, enligt beskrivningen i avsnittet "Versionshantering av meddelandekontrakt" senare i det här avsnittet. Däremot kan du läsa och skriva dessa attribut efter behov, som i följande beskrivning.
När du skickar ett meddelande genereras inte dessa attribut som standard. Du kan ändra detta på två sätt. Först kan du statiskt ange attributen till önskade värden genom att ändra MessageHeaderAttribute.Actoregenskaperna , MessageHeaderAttribute.MustUnderstandoch MessageHeaderAttribute.Relay , enligt följande kodexempel. (Observera att det inte finns någon Role
egenskap. Om du anger Actor egenskapen genereras Role
attributet om du använder 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;
}
Det andra sättet att styra dessa attribut är dynamiskt, via kod. Du kan uppnå detta genom att omsluta den önskade rubriktypen i MessageHeader<T> typen (se till att inte förväxla den här typen med den icke-generiska versionen) och genom att använda typen tillsammans med MessageHeaderAttribute. Sedan kan du använda egenskaper på MessageHeader<T> för att ange SOAP-attributen, som du ser i följande kodexempel.
[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;
Om du använder både dynamiska och statiska kontrollmekanismer används de statiska inställningarna som standard men kan senare åsidosättas med hjälp av den dynamiska mekanismen, som du ser i följande kod.
[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'
Det är tillåtet att skapa upprepade rubriker med dynamisk attributkontroll, enligt följande kod.
[MessageHeaderArray] public MessageHeader<Person> documentApprovers[];
På mottagarsidan kan du bara läsa dessa SOAP-attribut om MessageHeader<T> klassen används för rubriken i typen . Actor
Granska egenskaperna , Relay
eller MustUnderstand
för en rubrik av typen MessageHeader<T> för att identifiera attributinställningarna för det mottagna meddelandet.
När ett meddelande tas emot och sedan skickas tillbaka går inställningarna för SOAP-attributen endast tur och retur för sidhuvuden av typen MessageHeader<T> .
Ordningen på SOAP-kroppsdelar
I vissa fall kan du behöva kontrollera ordningen på kroppsdelarna. Ordningen på brödtextelementen är alfabetisk som standard, men kan styras av MessageBodyMemberAttribute.Order egenskapen. Den här egenskapen har samma semantik som DataMemberAttribute.Order egenskapen, förutom beteendet i arvsscenarier (i meddelandekontrakt sorteras inte brödtextmedlemmar av bastyp före brödtextmedlemmar av härledd typ). Mer information finns i Datamedlemsordning.
I följande exempel amount
skulle normalt komma först eftersom det är först alfabetiskt. Egenskapen placerar den Order dock i den tredje positionen.
[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;
}
Versionshantering av meddelandekontrakt
Ibland kan du behöva ändra meddelandekontrakt. En ny version av programmet kan till exempel lägga till en extra rubrik i ett meddelande. När du sedan skickar från den nya versionen till den gamla måste systemet hantera en extra rubrik, samt en rubrik som saknas när du går i den andra riktningen.
Följande regler gäller för versionshuvuden:
WCF invänder inte mot de saknade rubrikerna – motsvarande medlemmar lämnas vid sina standardvärden.
WCF ignorerar också oväntade extra rubriker. Ett undantag till den här regeln är om det extra huvudet har ett
MustUnderstand
attribut inställt påtrue
i det inkommande SOAP-meddelandet– i det här fallet utlöses ett undantag eftersom ett sidhuvud som måste tolkas inte kan bearbetas.
Meddelandeorgan har liknande versionsregler – både saknade och ytterligare delar av meddelandetexten ignoreras.
Arvsöverväganden
En typ av meddelandekontrakt kan ärva från en annan typ, så länge bastypen också har ett meddelandekontrakt.
När du skapar eller kommer åt ett meddelande med en meddelandekontraktstyp som ärver från andra typer av meddelandekontrakt gäller följande regler:
Alla meddelandehuvuden i arvshierarkin samlas in för att bilda hela uppsättningen rubriker för meddelandet.
Alla delar av meddelandetexten i arvshierarkin samlas in för att bilda hela meddelandetexten. Brödtextdelarna sorteras enligt de vanliga ordningsreglerna (efter MessageBodyMemberAttribute.Order egenskap och sedan alfabetisk), utan relevans för deras plats i arvshierarkin. Att använda arv av meddelandekontrakt där meddelandetextdelar förekommer på flera nivåer i arvsträdet rekommenderas inte. Om en basklass och en härledd klass definierar en rubrik eller en brödtextdel med samma namn, används medlemmen från klassen base-most för att lagra värdet för rubriken eller brödtextdelen.
Överväg klasserna i följande kodexempel.
[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;
}
Klassen PatientRecord
beskriver ett meddelande med en rubrik med namnet ID
. Rubriken motsvarar personID
och inte patientID
medlemmen, eftersom den grundläggande medlemmen väljs. Därför är fältet patientID
värdelöst i det här fallet. Brödtexten i meddelandet innehåller elementet diagnosis
följt av elementet patientName
, eftersom det är den alfabetiska ordningen. Observera att exemplet visar ett mönster som starkt avråder: både bas- och härledda meddelandekontrakt har delar av meddelandetexten.
WSDL-överväganden
När du genererar ett WSDL-kontrakt (Web Services Description Language) från en tjänst som använder meddelandekontrakt är det viktigt att komma ihåg att inte alla funktioner för meddelandekontrakt återspeglas i resulterande WSDL. Tänk också på följande faktorer:
WSDL kan inte uttrycka begreppet en matris med rubriker. När du skapar meddelanden med en matris med rubriker med hjälp MessageHeaderArrayAttributeav visar den resulterande WSDL bara en rubrik i stället för matrisen.
Det resulterande WSDL-dokumentet kanske inte återspeglar viss information på skyddsnivå.
Meddelandetypen som genereras i WSDL har samma namn som klassnamnet för meddelandekontraktstypen.
När du använder samma meddelandekontrakt i flera åtgärder genereras flera meddelandetyper i WSDL-dokumentet. Namnen görs unika genom att siffrorna "2", "3" och så vidare läggs till för efterföljande användning. När du importerar tillbaka WSDL skapas flera typer av meddelandekontrakt och är identiska förutom deras namn.
Överväganden för SOAP-kodning
Med WCF kan du använda den äldre SOAP-kodningsstilen för XML, men dess användning rekommenderas inte. När du använder det här formatet (genom att ange Use
egenskapen till Encoded
för det System.ServiceModel.XmlSerializerFormatAttribute som tillämpas på tjänstkontraktet) gäller följande ytterligare överväganden:
Meddelanderubrikerna stöds inte. Det innebär att attributet MessageHeaderAttribute och matrisattributet MessageHeaderArrayAttribute inte är kompatibla med SOAP-kodning.
Om meddelandekontraktet inte är omslutet, d.v.s. om egenskapen IsWrapped är inställd på
false
, kan meddelandekontraktet bara ha en brödtextdel.Namnet på omslutningselementet för begärandemeddelandekontraktet måste matcha åtgärdsnamnet.
WrapperName
Använd egenskapen för meddelandekontraktet för detta.Namnet på omslutningselementet för svarsmeddelandekontraktet måste vara samma som namnet på åtgärdens suffix som är "Svar". WrapperName Använd egenskapen för meddelandekontraktet för detta.
SOAP-kodning bevarar objektreferenser. Tänk till exempel på följande kod.
[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;
När du har serialiserat meddelandet med SOAP-kodning changedFrom
och changedTo
inte innehåller sina egna kopior av p
, utan pekar i stället på kopian inuti elementet changedBy
.
Saker att tänka på gällande prestanda
Varje meddelandehuvud och meddelandetextdel serialiseras oberoende av de andra. Därför kan samma namnområden deklareras igen för varje rubrik och brödtextdel. För att förbättra prestanda, särskilt när det gäller storleken på meddelandet på tråden, konsolidera flera rubriker och brödtextdelar i en enda rubrik eller brödtextdel. I stället för följande kod:
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageBodyMember] public Account sourceAccount;
[MessageBodyMember] public Account targetAccount;
[MessageBodyMember] public int amount;
}
Använd den här koden.
[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;
}
Händelsebaserade asynkrona kontrakt och meddelandekontrakt
Designriktlinjerna för det händelsebaserade asynkrona modelltillståndet att om mer än ett värde returneras returneras ett värde som Result
egenskapen och de andra returneras som egenskaper för EventArgs objektet. Ett resultat av detta är att om en klient importerar metadata med hjälp av de händelsebaserade asynkrona kommandoalternativen och åtgärden returnerar mer än ett värde, returnerar standardobjektet EventArgs ett värde som Result
egenskapen och resten är egenskaperna för EventArgs objektet.
Om du vill ta emot meddelandeobjektet som Result
egenskap och ha de returnerade värdena som egenskaper för objektet använder du kommandoalternativet /messageContract
. Detta genererar en signatur som returnerar svarsmeddelandet som Result
egenskapen på EventArgs objektet. Alla interna returvärden är sedan egenskaper för svarsmeddelandeobjektet.