Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Windows Communication Foundation (WCF) si můžete představit jako infrastrukturu zasílání zpráv. Může přijímat zprávy, zpracovávat je a odesílat do uživatelského kódu pro další akci, nebo může vytvářet zprávy z dat zadaných uživatelským kódem a doručovat je do cíle. Toto téma, které je určené pro pokročilé vývojáře, popisuje architekturu pro zpracování zpráv a obsažených dat. Jednodušší přehled o tom, jak odesílat a přijímat data, najdete v tématu Určení přenosu dat v kontraktech služeb.
Poznámka:
Toto téma popisuje podrobnosti implementace WCF, které nejsou viditelné prozkoumáním objektového modelu WCF. V souvislosti s zdokumentovanými podrobnostmi o implementaci jsou v pořádku dvě slova opatrnosti. Zaprvé jsou popisy zjednodušené; skutečná implementace může být složitější kvůli optimalizaci nebo jiným důvodům. Za druhé byste se nikdy neměli spoléhat na konkrétní podrobnosti implementace, ani na zdokumentované podrobnosti, protože se můžou změnit bez upozornění z verze na verzi nebo dokonce v servisní verzi.
Základní architektura
Jádrem schopností zpracování zpráv WCF je Message třída, která je podrobně popsána v použití třídy message. Komponenty modulu runtime WCF lze rozdělit do dvou hlavních částí: kanálového zásobníku a frameworku služby, přičemž třída Message je spojovacím bodem.
Zásobník kanálu zajišťuje konverzi mezi platnou instancí Message a akcí související s odesíláním nebo přijímáním dat zpráv. Na straně odesílání zásobník kanálu přijme platnou Message instanci a po určitém zpracování provede akci, která logicky odpovídá odeslání zprávy. Akce může posílat pakety TCP nebo HTTP, zapisovat zprávu do fronty ve službě Řízení front zpráv, zapisovat zprávu do databáze, ukládat ji do sdílené složky nebo jakoukoli jinou akci v závislosti na implementaci. Nejběžnější akcí je odeslání zprávy přes síťový protokol. Na straně příjmu dojde k opaku – zjistí se akce (například příchozí pakety TCP nebo HTTP nebo jakákoli jiná akce) a po zpracování kanálový zásobník tuto akci převede na platnou instanci Message.
WCF můžete použít přímo pomocí Message třídy a zásobníku kanálů. Je to ale obtížné a časově náročné. Objekt navíc Message neposkytuje žádnou podporu metadat, takže nelze generovat klienty WCF silného typu, pokud používáte WCF tímto způsobem.
WCF proto obsahuje rozhraní služby, které poskytuje snadno použitelný programovací model, který můžete použít k vytváření a přijímání Message objektů. Architektura služby mapuje služby na typy rozhraní .NET Framework prostřednictvím pojmů kontraktů služeb a odesílá zprávy do uživatelských operací, které jsou jednoduše metody rozhraní .NET Framework označené atributem OperationContractAttribute (další podrobnosti najdete v tématu Návrh kontraktů služeb). Tyto metody mohou mít parametry a návratové hodnoty. Na straně služby architektura služby převede příchozí Message instance na parametry a převede vrácené hodnoty na odchozí Message instance. Na straně klienta to dělá opačně. Například zvažte níže uvedenou operaci.
[ServiceContract]
public interface IAirfareFinderService
{
[OperationContract]
int FindAirfare(string FromCity, string ToCity, out bool IsDirectFlight);
}
<ServiceContract()> _
Public Interface IAirfareFinderService
<OperationContract()> _
Function FindAirfare(ByVal FromCity As String, _
ByVal ToCity As String, ByRef IsDirectFlight As Boolean) As Integer
End Interface
Předpokládejme, že FindAirfare je zavolána na klientovi. Architektura služby na klientovi převede parametry FromCity a ToCity do odchozí instance Message a předá ji do zásobníku kanálu pro odeslání.
Když instance dorazí ze zásobníku kanálu, rámec služby na straně Message extrahuje příslušná data ze zprávy, aby vyplnil parametry FromCity a ToCity a poté zavolá metodu FindAirfare na straně služby. Když metoda vrátí, rozhraní služby vezme vrácenou celočíselnou hodnotu a IsDirectFlight výstupní parametr a vytvoří instanci objektu Message , která obsahuje tyto informace. Pak předá instanci Message do zásobníku kanálu, aby ji odeslal zpět klientovi.
Na straně klienta se ze zásobníku kanálu objeví instance Message, která obsahuje zprávu odpovědi. Architektura služby extrahuje návratovou hodnotu a IsDirectFlight hodnotu a vrátí je volajícímu klienta.
Třída zprávy
Třída Message je určena jako abstraktní reprezentace zprávy, ale její návrh je silně svázaný se zprávou SOAP. A Message obsahuje tři hlavní informace: text zprávy, záhlaví zprávy a vlastnosti zprávy.
Text zprávy
Tělo zprávy je určeno k reprezentaci skutečného datového obsahu zprávy. Text zprávy je vždy reprezentován jako informační sada XML. To neznamená, že všechny zprávy vytvořené nebo přijaté ve WCF musí být ve formátu XML. Záleží na zásobníku kanálu, jak interpretovat tělo zprávy. Může ho generovat jako XML, převést ho do jiného formátu nebo dokonce zcela vynechat. Samozřejmě platí, že u většiny vazeb, které WCF poskytuje, je text zprávy reprezentován jako XML obsah v části těla obálky SOAP.
Je důležité si uvědomit, že třída Message nemusí nutně obsahovat vyrovnávací paměť s daty ve formátu XML představujícími tělo. Logicky Message obsahuje informační sadu XML, ale tato informační sada může být dynamicky vytvořena a nemusí nikdy fyzicky existovat v paměti.
Vložení dat do textu zprávy
Neexistuje žádný jednotný mechanismus pro vložení dat do textu zprávy. Třída Message má abstraktní metodu, OnWriteBodyContents(XmlDictionaryWriter)která přebírá XmlDictionaryWriter. Každá podtřída Message třídy zodpovídá za přepsání této metody a zapisování vlastního obsahu. Tělo zprávy logicky obsahuje xml infoset, který OnWriteBodyContent vytvoří. Představte si například následující Message podtřídu.
public class AirfareRequestMessage : Message
{
public string fromCity = "Tokyo";
public string toCity = "London";
//code omitted…
protected override void OnWriteBodyContents(XmlDictionaryWriter w)
{
w.WriteStartElement("airfareRequest");
w.WriteElementString("from", fromCity);
w.WriteElementString("to", toCity);
w.WriteEndElement();
}
public override MessageVersion Version
{
get { throw new NotImplementedException("The method is not implemented.") ; }
}
public override MessageProperties Properties
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override MessageHeaders Headers
{
get { throw new Exception("The method or operation is not implemented."); }
}
public override bool IsEmpty
{
get
{
return base.IsEmpty;
}
}
public override bool IsFault
{
get
{
return base.IsFault;
}
}
}
Public Class AirfareRequestMessage
Inherits Message
Public fromCity As String = "Tokyo"
Public toCity As String = "London"
' Code omitted…
Protected Overrides Sub OnWriteBodyContents(ByVal w As XmlDictionaryWriter)
w.WriteStartElement("airfareRequest")
w.WriteElementString("from", fromCity)
w.WriteElementString("to", toCity)
w.WriteEndElement()
End Sub
Public Overrides ReadOnly Property Version() As MessageVersion
Get
Throw New NotImplementedException("The method is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Properties() As MessageProperties
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property Headers() As MessageHeaders
Get
Throw New Exception("The method or operation is not implemented.")
End Get
End Property
Public Overrides ReadOnly Property IsEmpty() As Boolean
Get
Return MyBase.IsEmpty
End Get
End Property
Public Overrides ReadOnly Property IsFault() As Boolean
Get
Return MyBase.IsFault
End Get
End Property
End Class
Fyzicky instance AirfareRequestMessage obsahuje pouze dva řetězce, "fromCity" a "toCity". Logicky však zpráva obsahuje následující informační sadu XML:
<airfareRequest>
<from>Tokyo</from>
<to>London</to>
</airfareRequest>
Samozřejmě byste normálně nevytvářeli zprávy tímto způsobem, protože můžete použít rámec služby k vytvoření zprávy podobné té předchozí z parametrů kontraktu operace. Kromě toho třída má statické metody, Message které můžete použít k vytváření zpráv s běžnými typy obsahu: prázdná zpráva, zpráva obsahující objekt serializovaný do XML s CreateMessage, zpráva obsahující chybu SOAP, zprávu obsahující XML reprezentovaný objektem DataContractSerializerXmlReader atd.
Získání dat z textu zprávy
Data uložená v textu zprávy můžete extrahovat dvěma hlavními způsoby:
Celý text zprávy můžete získat najednou voláním WriteBodyContents(XmlDictionaryWriter) metody a předáním zapisovače XML. Úplné znění zprávy je zapsáno do tohoto zapisovače. Získání celého textu zprávy najednou se označuje také jako zápis zprávy. Psaní se provádí především zásobníkem kanálu při odesílání zpráv – některá část zásobníku kanálu obvykle získá přístup k celému textu zprávy, zakóduje ho a odešle.
Dalším způsobem, jak získat informace z textu zprávy, je volání GetReaderAtBodyContents() a získání čtečky XML. Tělo zprávy lze následně zpřístupnit postupným voláním metod u čtenáře podle potřeby. Postupné získávání těla zprávy se také označuje jako čtení zprávy. Čtení zprávy je primárně používáno rozhraním služby při přijímání zpráv. Pokud se například DataContractSerializer používá, rozhraní služby získá čtečku XML nad tělem a předá ji modulu deserializace, který pak začne číst element zprávy po elementu a sestavení odpovídajícího objektového grafu.
Text zprávy lze načíst pouze jednou. To umožňuje pracovat s jednosměrnými proudy. Můžete například napsat OnWriteBodyContents(XmlDictionaryWriter) překrytí, které čte z FileStream a vrátí výsledky jako informační sadu XML. Nikdy nebudete muset "převinout zpět" na začátek souboru.
Metody WriteBodyContents a GetReaderAtBodyContents jednoduše zkontrolují, že tělo zprávy nebylo nikdy načteno, a pak zavolají OnWriteBodyContents nebo OnGetReaderAtBodyContents, respektive, v uvedeném pořadí.
Použití zpráv ve WCF
Většinu zpráv je možné klasifikovat jako odchozí (ty, které jsou vytvořeny rozhraním služby, které se mají odesílat zásobníkem kanálu) nebo jako příchozí (ty, které přicházejí ze zásobníku kanálu a jsou interpretovány rozhraním služby). Zásobník kanálů navíc může fungovat buď v režimu ukládání do vyrovnávací paměti, nebo v režimu streamování. Architektura služby může také vystavit streamovaný nebo nestreamovaný programovací model. To vede k případům uvedeným v následující tabulce spolu se zjednodušenými podrobnostmi o jejich implementaci.
| Typ zprávy | Data těla ve zprávě | Implementace Write (OnWriteBodyContents) | Implementace čtení (OnGetReaderAtBodyContents) |
|---|---|---|---|
| Odchozí, vytvořené pomocí nestreamového programovacího modelu | Data potřebná k zápisu zprávy (například objekt a DataContractSerializer instance potřebné k jeho serializaci)* | Vlastní logika pro zapsání zprávy podle uložených dat (například volání WriteObject na DataContractSerializer , pokud je to právě používaný serializátor)* |
Volání OnWriteBodyContents, uložení výsledků do vyrovnávací paměti, vrácení čtečky XML do vyrovnávací paměti |
| Odchozí, vytvořené ze streamového programovacího modelu |
Stream data, která se mají zapsat |
Zapisování dat z uloženého datového proudu pomocí IStreamProvider mechanismu* | Volání OnWriteBodyContents, uložení výsledků do vyrovnávací paměti, vrácení čtečky XML do vyrovnávací paměti |
| Příchozí ze stacku dat streamovaných kanálů | Objekt Stream, který představuje data přicházející přes síť, na kterých je XmlReader. |
Zapsat obsah z uloženého XmlReader pomocí WriteNode |
Vrátí uloženou hodnotu. XmlReader |
| Příchozí ze zásobníku kanálů bez streamování | Vyrovnávací paměť, která obsahuje tělová data s XmlReader nad nimi |
Zapíše obsah z uloženého XmlReader pomocí příkazu WriteNode |
Vrátí uložený jazyk. |
* Tyto položky nejsou implementovány přímo v Message podtřídách, ale v podtřídách BodyWriter třídy. Další informace o této BodyWritertřídě naleznete v tématu Použití třídy zprávy.
Záhlaví zpráv
Zpráva může obsahovat záhlaví. Hlavička se logicky skládá z informační sady XML, která je přidružena k názvu, oboru názvů a několika dalším vlastnostem. K záhlavím zpráv se přistupuje pomocí vlastnosti Headers na Message. Každé záhlaví je reprezentováno MessageHeader třídou. Záhlaví zpráv se obvykle mapují na záhlaví zpráv SOAP, když se používá zásobník kanálu nakonfigurovaný pro práci se zprávami SOAP.
Vložení informací do záhlaví zprávy a extrakce informací z ní se podobá použití textu zprávy. Proces je poněkud zjednodušený, protože streamování se nepodporuje. Je možné přistupovat k obsahu téže hlavičky více než jednou a k záhlavím se může přistupovat v libovolném pořadí, což nutí hlavičky vždy ukládat do vyrovnávací paměti. Neexistuje žádný obecně použitelný mechanismus k získání čtečky XML nad hlavičkou, ale existuje MessageHeader interní podtřída WCF, která představuje čitelnou hlavičku s takovou schopností. Tento typ MessageHeader je vytvořen zásobníkem kanálu, když přijde zpráva s vlastními hlavičkami aplikace. To umožňuje rozhraní služby použít deserializační modul, například DataContractSerializer, k interpretaci těchto hlaviček.
Další informace naleznete v tématu Použití třídy zprávy.
Vlastnosti zprávy
Zpráva může obsahovat vlastnosti.
Vlastnost je jakýkoli objekt rozhraní .NET Framework, který je přidružen k názvu řetězce. K vlastnostem se přistupuje přes Properties na Message.
Na rozdíl od textu zprávy a záhlaví zprávy (které se obvykle mapují na tělo SOAP a hlavičky SOAP), vlastnosti zprávy se spolu se zprávami obvykle neodesílají ani nepřijímají. Vlastnosti zpráv existují primárně jako komunikační mechanismus pro předávání dat o zprávě mezi různými kanály v zásobníku kanálů a mezi zásobníkem kanálů a modelem služby.
Například přenosový kanál HTTP zahrnutý jako součást WCF dokáže vytvářet různé stavové kódy HTTP, například 404 (Nenalezena) a 500 (vnitřní chyba serveru), když odesílá odpovědi klientům. Před odesláním zprávy odpovědi zkontroluje, zda PropertiesMessage obsahují vlastnost s názvem "httpResponse", která obsahuje objekt typu HttpResponseMessageProperty. Pokud se taková vlastnost najde, podívá se na StatusCode vlastnost a použije tento stavový kód. Pokud se nenajde, použije se výchozí kód 200 (OK).
Další informace naleznete v tématu Použití třídy zprávy.
Zpráva jako celek
Zatím jsme probrali metody pro přístup k různým částem zprávy izolovaně.
Message Třída však také poskytuje metody pro práci s celou zprávou. Například WriteMessage metoda zapisuje celou zprávu do zapisovače XML.
Aby bylo možné, musí být mapování definováno mezi celou Message instancí a xml infoset. Takové mapování ve skutečnosti existuje: WCF k definování tohoto mapování používá standard SOAP.
Message Když je instance zapsána jako XML Infoset, výsledný infoset je platná obálka SOAP, která obsahuje zprávu.
WriteMessage Obvykle by tedy prováděl následující kroky:
Napište počáteční značku elementu obálky SOAP.
Napište otevírací značku elementu záhlaví SOAP, zapište všechna záhlaví a zavřete element záhlaví.
Napište počáteční značku základního prvku SOAP.
Volání
WriteBodyContentsnebo ekvivalentní metoda pro zápis textu.Zavřete tělo a prvky obálky.
Předchozí kroky jsou úzce svázané se standardem SOAP. To je komplikované skutečností, že existuje více verzí protokolu SOAP, například není možné správně zapsat prvek obálky SOAP bez znalosti používané verze PROTOKOLU SOAP. V některých případech může být také žádoucí úplně vypnout toto komplexní mapování specifické pro SOAP.
Pro tyto účely je na Version poskytována vlastnost Message. Dá se nastavit na verzi PROTOKOLU SOAP, která se má použít při zápisu zprávy, nebo může být nastavená tak, aby None se zabránilo mapování specifické pro protokol SOAP. Pokud je vlastnost nastavena Version na None, metody, které pracují s celou zprávou fungují, jako by se zpráva skládala pouze z textu, například by WriteMessage jednoduše volala WriteBodyContents místo provádění více kroků uvedených výše. Očekává se, Version že u příchozích zpráv se automaticky rozpozná a nastaví správně.
Vrstva kanálů
Kanály
Jak je uvedeno dříve, zásobník kanálu zodpovídá za převod odchozích Message instancí na nějakou akci (například odesílání paketů přes síť) nebo převod určité akce (například příjem síťových paketů) na příchozí Message instance.
Zásobník kanálů se skládá z jednoho nebo více kanálů uspořádaných v pořadí. Odchozí Message instance se předává do prvního kanálu v zásobníku (označovaný také jako nejvyšší kanál), který ho předává do dalšího kanálu v zásobníku a tak dále. Zpráva se ukončí v posledním kanálu, který se nazývá přenosový kanál. Příchozí zprávy pocházejí z přenosového kanálu a předávají se z kanálu do zásobníku. Z nejvyššího kanálu se zpráva obvykle předává do architektury služby. I když se jedná o obvyklý vzor pro zprávy aplikace, některé kanály můžou fungovat trochu jinak, například můžou posílat vlastní zprávy infrastruktury, aniž by byly předány zprávy z výše uvedeného kanálu.
Kanály můžou na zprávě pracovat různými způsoby, jak prochází zásobníkem. Nejběžnější operací je přidání záhlaví do odchozí zprávy a čtení hlaviček příchozí zprávy. Kanál může například vypočítat digitální podpis zprávy a přidat ji jako záhlaví. Kanál může také zkontrolovat záhlaví digitálního podpisu u příchozích zpráv a blokovat ty, které nemají platný podpis, aby nemohly projít do kanálového zásobníku. Kanály také často nastavují nebo kontrolují vlastnosti zpráv. Text zprávy se obvykle nezmění, i když je to povolené, například kanál zabezpečení WCF může šifrovat text zprávy.
Přenosové kanály a kodéry zpráv
Nejspodnější kanál v zásobníku je zodpovědný za skutečnou transformaci odchozího Message, upraveného jinými kanály, na nějakou akci. Na straně příjmu se jedná o kanál, který převede určitou akci na Message, což pak zpracovávají jiné kanály.
Jak bylo uvedeno dříve, akce se mohou lišit: odesílání nebo příjem síťových paketů přes různé protokoly, čtení nebo zápis zprávy do databáze nebo zařazení či vyřazení zprávy ve frontě Message Queuing, abychom uvedli jen několik příkladů. Všechny tyto akce mají jednu věc společného: vyžadují transformaci mezi instancí WCFMessage a skutečnou skupinou bajtů, které je možné odesílat, přijímat, číst, zapisovat, zapsat, zařadit do fronty nebo odložit. Proces převodu na Message skupinu bajtů se nazývá kódování a reverzní proces vytvoření Message ze skupiny bajtů se nazývá dekódování.
Většina přenosových kanálů používá komponenty označované jako kodéry zpráv k provádění kódování a dekódování. Kodér zpráv je podtřídou MessageEncoder třídy.
MessageEncoder zahrnuje různá ReadMessage přetížení a WriteMessage přetížení metod pro převod mezi Message bajty a skupinami bajtů.
Na vysílací straně vyrovnávací přenosový kanál předá Message objekt, který přijal z kanálu nad nímWriteMessage. Získá zpět pole bajtů, které pak použije k provedení své akce (například zabalení těchto bajtů jako platných paketů TCP a jejich odeslání do správného cíle). Streamovací přenosový kanál nejprve vytvoří Stream (například přes odchozí připojení TCP), a pak předá jak Stream, tak Message, které potřebuje odeslat do příslušného WriteMessage přetížení, které zprávu zapíše.
Na straně příjmu extrahuje transportní kanál do vyrovnávací paměti příchozí bajty (například z příchozích paketů TCP) do pole a volání ReadMessage pro získání objektu Message , který může předat dále zásobník kanálu. Přenosový kanál streamování vytvoří Stream objekt (například síťový stream přes příchozí připojení TCP) a předá ho do ReadMessage, aby se získal zpět Message objekt.
Oddělení mezi přenosovými kanály a kodérem zpráv není povinné; Je možné napsat přenosový kanál, který nepoužívá kodér zpráv. Výhodou tohoto rozdělení je však snadná kompozice. Pokud přenosový kanál používá pouze základ MessageEncoder, může pracovat s libovolným kodérem WCF nebo kódem zpráv třetích stran. Stejně tak stejný kodér lze normálně použít v libovolném přenosovém kanálu.
Operace kodéru zpráv
Pokud chcete popsat typickou operaci kodéru, je vhodné vzít v úvahu následující čtyři případy.
| Operace | Komentář |
|---|---|
| Kódování, ukládání do vyrovnávací paměti | V režimu vyrovnávací paměti kodér obvykle vytvoří vyrovnávací paměť o proměnlivé velikosti a poté vytvoří zapisovač XML. Poté volá WriteMessage(XmlWriter) na zprávě, která se kóduje, což zapisuje hlavičky a pak tělo pomocí WriteBodyContents(XmlDictionaryWriter) tak, jak je vysvětleno v předcházející části o Message v tomto tématu. Obsah vyrovnávací paměti (reprezentovaný jako pole bajtů) se pak vrátí, aby jej mohl přenosový kanál použít. |
| Kódování, streamování | V režimu streamování se operace podobá výše uvedenému, ale jednoduššímu. Není potřeba vyrovnávací paměť. Zapisovač XML je obvykle vytvořen přes datový proud a WriteMessage(XmlWriter) je volána Message k jeho zápisu do tohoto zapisovače. |
| Dekódování, ukládání do vyrovnávací paměti | Při dekódování v režimu vyrovnávací paměti se obvykle vytvoří speciální Message podtřída obsahující uložená data ve vyrovnávací paměti. Záhlaví zprávy se čtou a vytvoří se čtečka XML umístěná v textu zprávy. To je čtenář, který bude vrácen s GetReaderAtBodyContents(). |
| Streamované dekódování | Při dekódování v streamovaném režimu se obvykle vytvoří speciální podtřída zprávy. Stream je dostatečně pokročilý, aby přečetl všechna záhlaví a umístil ho do textu zprávy. Čtečka XML se pak vytvoří přes stream. To je čtenář, který bude vrácen s GetReaderAtBodyContents(). |
Kodéry můžou také provádět další funkce. Enkodéry mohou například sdružit čtečky a zapisovače XML. Vytvoření nové čtečky XML nebo zapisovače je nákladné pokaždé, když je potřeba. Kodéry proto obvykle udržují fond čtenářů a fond zapisovačů konfigurovatelné velikosti. V popisech operace kodéru uvedených dříve, kdykoli se použije fráze "vytvořit čtečku/zapisovače XML", obvykle to znamená "vzít jeden z fondu, nebo vytvořit jeden, pokud není k dispozici." Kodér (a Message podtřídy, které vytváří během dekódování) obsahuje logiku pro vrácení čteček a zapisovačů do fondů, jakmile už nejsou potřeba (například když je Message zavřen).
WCF poskytuje tři kodéry zpráv, i když je možné vytvořit další vlastní typy. Zadané typy jsou Text, Binary a Message Transmission Optimization Mechanism (MTOM). Tyto informace jsou podrobně popsány při výběru kodéru zpráv.
IStreamProvider – rozhraní
Při zápisu odchozí zprávy, která obsahuje streamované tělo do zapisovače XML, Message používá posloupnost volání podobná následující v jeho OnWriteBodyContents(XmlDictionaryWriter) implementaci:
Zapište všechny potřebné informace před streamem (například počáteční značku XML).
Napište stream.
Zapište všechny informace za streamem (například zavírací značku XML).
To funguje dobře s kódováními, které jsou podobné textovému kódování XML. Některá kódování však neumisťují informace XML Infoset (například značky pro počáteční a koncové elementy XML) spolu s daty obsaženými v elementech. Například v kódování MTOM je zpráva rozdělena do více částí. Jedna část obsahuje informační sadu XML, která může obsahovat odkazy na jiné části pro skutečný obsah elementu. Informační sada XML je obvykle malá ve srovnání s streamovaným obsahem, takže dává smysl ukládat informace do vyrovnávací paměti, zapisovat je a pak psát obsah streamovaným způsobem. To znamená, že po napsání značky uzavíracího prvku by stream ještě neměl být zapsán.
Pro tento účel se používá rozhraní IStreamProvider. Rozhraní má metodu GetStream() , která vrací datový proud, který má být zapsán. Správný způsob, jak napsat text streamované zprávy v OnWriteBodyContents(XmlDictionaryWriter), je tento:
Zapište všechny potřebné informace před streamem (například počáteční značku XML).
Zavolejte přetížení ve
WriteValues příkazem XmlDictionaryWriter a implementací IStreamProvider, která vrací datový proud k zápisu.Zapište všechny informace za streamem (například zavírací značku XML).
Při tomto přístupu má zapisovač XML možnost, kdy volat GetStream() a zapisovat streamovaná data. Textové a binární zapisovače XML ho například okamžitě volají a zapisují streamovaný obsah mezi počátečními a koncovými značkami. Zapisovač MTOM se může rozhodnout zavolat GetStream() později, až bude připraven napsat příslušnou část zprávy.
Reprezentace dat v rozhraní service Framework
Jak je uvedeno v části Základní architektura tohoto tématu, architektura služby je součástí WCF, která mimo jiné zodpovídá za převod mezi uživatelsky přívětivým programovacím modelem pro data zpráv a skutečné Message instance. Za normálních okolností je výměna zpráv reprezentována v rozhraní služby jako metoda rozhraní .NET Framework označená atributem OperationContractAttribute . Metoda může převzít některé parametry a může vrátit návratovou hodnotu i výstupní parametry (nebo obojí). Na straně služby vstupní parametry představují příchozí zprávu a návratové a výstupní parametry představují odchozí zprávu. Na straně klienta je pravý opak. Programovací model popisující zprávy pomocí parametrů a návratová hodnota je podrobně popsána v části Určení přenosu dat v kontraktech služeb. Tato část ale poskytne stručný přehled.
Programovací modely
Architektura služby WCF podporuje pět různých programovacích modelů pro popis zpráv:
1. Prázdná zpráva
To je nejjednodušší případ. K popisu prázdné příchozí zprávy nepoužívejte žádné vstupní parametry.
[OperationContract]
int GetCurrentTemperature();
<OperationContract()> Function GetCurrentTemperature() As Integer
Pokud chcete popsat prázdnou odchozí zprávu, použijte návratovou hodnotu void a nepoužívejte žádné výstupní parametry:
[OperationContract]
void SetDesiredTemperature(int t);
<OperationContract()> Sub SetDesiredTemperature(ByVal t As Integer)
Všimněte si, že se liší od jednosměrné smlouvy o operaci:
[OperationContract(IsOneWay = true)]
void SetLightbulb(bool isOn);
<OperationContract(IsOneWay:=True)> Sub SetLightbulb(ByVal isOn As Boolean)
V tomto příkladu SetDesiredTemperature je popsán obousměrný vzor výměny zpráv. Z operace se vrátí zpráva, ale je prázdná. Při operaci je možné vrátit chybu. V příkladu Nastavit žárovku je vzor výměny zpráv jednosměrný, takže neexistuje žádná odchozí zpráva, kterou byste chtěli popsat. Služba nemůže v tomto případě klientovi sdělit žádný stav.
2. Přímé použití třídy zpráv
Třídu (nebo jednu z jejích podtříd) je možné použít Message přímo v kontraktu operace. V tomto případě rámec služby prostě předává Message z operace do zásobníku kanálu a obráceně, bez dalšího zpracování.
Existují dva hlavní případy použití pro přímé použití Message . Tuto možnost můžete použít pro pokročilé scénáře, když žádný z ostatních programovacích modelů neposkytuje dostatečnou flexibilitu k popisu zprávy. Můžete například chtít použít soubory na disku k popisu zprávy, přičemž vlastnosti souboru se stávají záhlavími zpráv a obsahem souboru se stává textem zprávy. Pak můžete vytvořit něco podobného jako v následujícím příkladu.
public class FileMessage : Message
// Code not shown.
Public Class FileMessage
Inherits Message
' Code not shown.
Druhým běžným použitím Message v kontraktu operace je, když služba nedbá na konkrétní obsah zprávy a pracuje se zprávou jako s černou skříňkou. Můžete mít například službu, která přeposílala zprávy více dalším příjemcům. Smlouvu lze napsat následujícím způsobem.
[OperationContract]
public FileMessage GetFile()
{
//code omitted…
FileMessage fm = new FileMessage("myFile.xml");
return fm;
}
<OperationContract()> Public Function GetFile() As FileMessage
'code omitted…
Dim fm As New FileMessage("myFile.xml")
Return fm
End Function
Řádek Action="*" efektivně vypne přenášení zpráv a zajistí, že všechny zprávy odeslané ke kontraktu IForwardingService se dostanou k operaci ForwardMessage. (Dispečer obvykle zkontroluje hlavičku akce zprávy a určí, pro kterou operaci je určená. Action="*" znamená "všechny možné hodnoty záhlaví akce".) Kombinace action="*" a použití zprávy jako parametru se označuje jako "univerzální kontrakt", protože dokáže přijímat všechny možné zprávy. Pokud chcete být schopni odesílat všechny možné zprávy, použijte jako návratovou hodnotu Message a nastavte ReplyAction na *. Tím zabráníte, aby architektura služby přidala vlastní hlavičku akce, což vám umožní řídit tuto hlavičku pomocí vráceného objektu Message .
3. Smlouvy o zprávách
WCF poskytuje deklarativní programovací model pro popis zpráv označovaný jako kontrakty zpráv. Tento model je podrobně popsán v části Používání kontraktů zpráv. V podstatě je celá zpráva reprezentována jedním typem rozhraní .NET Framework, který používá atributy jako MessageBodyMemberAttribute a MessageHeaderAttribute popisuje, které části třídy kontraktu zpráv by se měly mapovat na kterou část zprávy.
Kontrakty zpráv poskytují velkou kontrolu nad výslednými Message instancemi (i když samozřejmě není tolik ovládacích prvků, jako je použití Message třídy přímo). Například těla zpráv se často skládají z více informací, z nichž každý představuje vlastní prvek XML. Tyto prvky se mohou vyskytovat buď přímo v těle (holý režim), nebo mohou být zabaleny do zahrnujícího XML elementu. Použití programovacího modelu kontraktu zpráv umožňuje provést rozhodnutí mezi holým a zabaleným formátem a řídit název obálky a obor názvů.
Následující příklad kódu kontraktu zprávy ukazuje tyto funkce.
[MessageContract(IsWrapped = true, WrapperName = "Order")]
public class SubmitOrderMessage
{
[MessageHeader]
public string customerID;
[MessageBodyMember]
public string item;
[MessageBodyMember]
public int quantity;
}
<MessageContract(IsWrapped:=True, WrapperName:="Order")> _
Public Class SubmitOrderMessage
<MessageHeader()> Public customerID As String
<MessageBodyMember()> Public item As String
<MessageBodyMember()> Public quantity As Integer
End Class
Položky označené k serializaci (s MessageBodyMemberAttribute, MessageHeaderAttributenebo jinými souvisejícími atributy) musí být serializovatelné pro účast ve smlouvě zprávy. Další informace naleznete v části Serializace dále v tomto tématu.
4. Parametry
Vývojář, který chce často popsat operaci, která funguje na více částech dat, nepotřebuje stupeň kontroly, kterou kontrakty zpráv poskytují. Například při vytváření nových služeb se obvykle nechce rozhodovat, zda použít holý nebo zabalený formát, a určovat název obalového prvku. Tato rozhodnutí často vyžadují hluboké znalosti webových služeb a protokolu SOAP.
Architektura služby WCF může automaticky vybrat nejlepší a nejoperabilní reprezentaci PROTOKOLU SOAP pro odesílání nebo přijímání více souvisejících informací bez vynucení těchto voleb pro uživatele. Toho dosáhnete jednoduchým popisem těchto informací jako parametrů nebo návratovými hodnotami kontraktu operace. Představte si například následující kontrakt operace.
[OperationContract]
void SubmitOrder(string customerID, string item, int quantity);
<OperationContract()> _
Sub SubmitOrder( _
ByVal customerID As String, _
ByVal item As String, _
ByVal quantity As Integer)
Architektura služby se automaticky rozhodne vložit všechny tři části informací (customerID, itema quantity) do textu zprávy a zabalit je do elementu obálky s názvem SubmitOrderRequest.
Popisování informace, které mají být poslány nebo přijaty jako jednoduchý seznam parametrů smlouvy o operaci je doporučeným přístupem, pokud neexistují zvláštní důvody pro přechod na složitější kontrakt založený na zprávě nebo programovací modely vycházející z Message.
5. Stream
Použití Stream nebo jedné z jeho podtříd v kontraktu operace nebo jako jedinou část těla zprávy v kontraktu zprávy lze považovat za samostatný programovací model od výše popsaných. Použití Stream tímto způsobem je jedinou metodou, jak zaručit, že vaše smlouva bude použitelná pro streamování, a to až na napsání vlastní podtřídy kompatibilní se streamováním pro Message. Další informace najdete v tématu Velké objemy dat a streamování.
Pokud je Stream nebo jedna z jejích podtříd použita tímto způsobem, serializátor se nevyvolá. U odchozích zpráv se vytvoří speciální podtřída streamování Message a datový proud se zapíše, jak je popsáno v části rozhraní IStreamProvider . Pro příchozí zprávy rámec služby vytvoří podtřídu na základě příchozí zprávy a poskytne ji operaci.
Omezení programovacího modelu
Programovací modely popsané výše nelze libovolně kombinovat. Pokud například operace přijme typ kontraktu zprávy, musí být kontrakt zprávy jeho jediným vstupním parametrem. Kromě toho musí operace vrátit prázdnou zprávu (návratový typ void) nebo jiný kontrakt zprávy. Tato omezení programovacího modelu jsou popsána v tématech pro každý konkrétní programovací model: Použití kontraktů zpráv, použití třídy zpráv a velkých dat a streamování.
Formátování zpráv
Programovací modely popsané výše jsou podporovány připojením komponent, které se nazývají formátování zpráv do architektury služby. Formátovače zpráv jsou typy, které implementují rozhraní IClientMessageFormatter, IDispatchMessageFormatter nebo obojí, pro použití v klientech a klientech služeb WCF.
Formátovací moduly zpráv jsou obvykle zapojeny podle chování. Například DataContractSerializerOperationBehavior zapojí modul formátování zpráv datové smlouvy. To se provádí na straně služby nastavením Formatter správného formátovače v ApplyDispatchBehavior(OperationDescription, DispatchOperation) metodě nebo na straně klienta nastavením Formatter na správný formátovač v ApplyClientBehavior(OperationDescription, ClientOperation) metodě.
Následující tabulky uvádí metody, které může implementovat formátovač zpráv.
| Rozhraní | Metoda | Činnost |
|---|---|---|
| IDispatchMessageFormatter | DeserializeRequest(Message, Object[]) | Převede příchozí Message parametry na parametry operace. |
| IDispatchMessageFormatter | SerializeReply(MessageVersion, Object[], Object) | Vytvoří odchozí Message z návratové hodnoty nebo výstupních parametrů operace. |
| IClientMessageFormatter | SerializeRequest(MessageVersion, Object[]) | Vytvoří odchozí Message z parametrů operace. |
| IClientMessageFormatter | DeserializeReply(Message, Object[]) | Převede příchozí Message parametry na návratovou hodnotu nebo out. |
Serializace
Při každém použití kontraktů zpráv nebo parametrů k popisu obsahu zprávy je nutné použít serializaci k převodu mezi typy rozhraní .NET Framework a reprezentací XML Infoset. Serializace se používá na jiných místech v WCF, například Message má metodu GetBody, kterou můžete použít ke čtení celého textu zprávy deserializované do objektu.
WCF podporuje dvě technologie serializace „bez nutnosti konfigurace“ pro serializaci a deserializaci parametrů a částí zpráv: DataContractSerializer a XmlSerializer. Kromě toho můžete psát vlastní serializátory. Jiné části WCF (například obecná GetBody metoda nebo serializace chyb SOAP) však mohou být omezeny na použití pouze XmlObjectSerializer podtřídy (DataContractSerializer a NetDataContractSerializer, ale ne XmlSerializer), nebo mohou být dokonce pevně zakódovány pouze DataContractSerializer.
Jedná se XmlSerializer o serializační modul používaný ve webových službách ASP.NET. Jedná se DataContractSerializer o nový serializační modul, který rozumí novému programovacímu modelu kontraktu dat.
DataContractSerializer je výchozí volba a možnost použití XmlSerializer lze provést na základě jednotlivých operací pomocí atributu DataContractFormatAttribute .
DataContractSerializerOperationBehavior a XmlSerializerOperationBehavior jsou provozní chování zodpovědná za připojení formátovačů zpráv k DataContractSerializer a XmlSerializer. Chování DataContractSerializerOperationBehavior může ve skutečnosti pracovat s jakýmkoliv serializátorem, který je odvozen od XmlObjectSerializer, včetně NetDataContractSerializer (podrobně popsáno v části Použití Stand-Alone serializace). Chování volá jedno z CreateSerializer přetížení virtuální metody k získání serializátoru. Pokud chcete připojit jiný serializátor, vytvořte novou DataContractSerializerOperationBehavior podtřídu a přepište obě CreateSerializer přetížení.