Architectuuroverzicht van gegevensoverdracht
Windows Communication Foundation (WCF) kan worden beschouwd als een berichteninfrastructuur. Het kan berichten ontvangen, verwerken en verzenden naar gebruikerscode voor verdere actie, of het kan berichten samenstellen van gegevens die zijn opgegeven door gebruikerscode en ze naar een bestemming leveren. In dit onderwerp, dat is bedoeld voor geavanceerde ontwikkelaars, wordt de architectuur beschreven voor het verwerken van berichten en de ingesloten gegevens. Zie Gegevensoverdracht opgeven in servicecontracten voor een eenvoudigere, taakgerichte weergave van het verzenden en ontvangen van gegevens.
Notitie
In dit onderwerp worden wcf-implementatiedetails besproken die niet zichtbaar zijn door het WCF-objectmodel te onderzoeken. Er zijn twee voorzichtige woorden met betrekking tot gedocumenteerde implementatiedetails. Ten eerste zijn de beschrijvingen vereenvoudigd; de daadwerkelijke implementatie kan complexer zijn vanwege optimalisaties of andere redenen. Ten tweede moet u nooit afhankelijk zijn van specifieke implementatiedetails, zelfs gedocumenteerde, omdat deze kunnen veranderen zonder kennisgeving van versie naar versie of zelfs in een onderhoudsrelease.
Basisarchitectuur
De kern van de WCF-mogelijkheden voor het verwerken van berichten is de Message klasse, die in detail wordt beschreven in Het gebruik van de berichtklasse. De runtimeonderdelen van WCF kunnen worden onderverdeeld in twee belangrijke onderdelen: de kanaalstack en het serviceframework, waarbij de Message klasse het verbindingspunt is.
De kanaalstack is verantwoordelijk voor het converteren tussen een geldig exemplaar Message en een actie die overeenkomt met het verzenden of ontvangen van berichtgegevens. Aan de verzendzijde neemt de kanaalstack een geldig exemplaar Message en voert na enige verwerking een actie uit die logisch overeenkomt met het verzenden van het bericht. De actie kan TCP- of HTTP-pakketten verzenden, het bericht in de wachtrij plaatsen in Message Queuing, het bericht naar een database schrijven, opslaan in een bestandsshare of een andere actie, afhankelijk van de implementatie. De meest voorkomende actie is het verzenden van het bericht via een netwerkprotocol. Aan de ontvangstzijde gebeurt het tegenovergestelde: er wordt een actie gedetecteerd (dit kunnen TCP- of HTTP-pakketten zijn die binnenkomen of een andere actie) en na verwerking converteert de kanaalstack deze actie naar een geldig exemplaar Message .
U kunt WCF rechtstreeks gebruiken met behulp van de Message klasse en de kanaalstack. Dit is echter moeilijk en tijdrovend. Daarnaast biedt het Message object geen ondersteuning voor metagegevens, dus u kunt geen sterk getypte WCF-clients genereren als u WCF op deze manier gebruikt.
WCF bevat daarom een serviceframework dat een gebruiksvriendelijk programmeermodel biedt dat u kunt gebruiken om objecten te maken en te ontvangen Message
. Het serviceframework wijst services toe aan .NET Framework-typen via het begrip servicecontracten en verzendt berichten naar gebruikersbewerkingen die eenvoudigweg .NET Framework-methoden zijn die zijn gemarkeerd met het OperationContractAttribute kenmerk (zie Servicecontracten ontwerpen voor meer informatie). Deze methoden hebben mogelijk parameters en retourwaarden. Aan de servicezijde converteert het serviceframework binnenkomende Message exemplaren naar parameters en converteert retourwaarden naar uitgaande Message exemplaren. Aan de clientzijde doet het het tegenovergestelde. Bekijk bijvoorbeeld de FindAirfare
onderstaande bewerking.
[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
Stel dat FindAirfare
de client wordt aangeroepen. Het serviceframework op de client converteert de FromCity
en ToCity
parameters naar een uitgaand Message exemplaar en geeft deze door aan de kanaalstack die moet worden verzonden.
Wanneer een Message exemplaar vanuit de kanaalstack binnenkomt, haalt het serviceframework de relevante gegevens uit het bericht op om de FromCity
en ToCity
parameters te vullen en roept vervolgens de methode aan de servicezijde FindAirfare
aan. Wanneer de methode wordt geretourneerd, neemt het serviceframework de geretourneerde geheel getalwaarde en de IsDirectFlight
uitvoerparameter en maakt het een Message objectexemplaren die deze informatie bevat. Vervolgens wordt het Message
exemplaar doorgegeven aan de kanaalstack die naar de client wordt verzonden.
Aan de clientzijde komt een Message exemplaar met het antwoordbericht uit de kanaalstack voor. Het serviceframework extraheert de retourwaarde en de IsDirectFlight
waarde en retourneert deze aan de aanroeper van de client.
Berichtklasse
De Message klasse is bedoeld als een abstracte weergave van een bericht, maar het ontwerp is sterk gekoppeld aan het SOAP-bericht. A Message bevat drie belangrijke gegevens: een berichttekst, berichtkoppen en berichteigenschappen.
Berichttekst
De hoofdtekst van het bericht is bedoeld om de werkelijke nettolading van het bericht weer te geven. De hoofdtekst van het bericht wordt altijd weergegeven als een XML-infoset. Dit betekent niet dat alle berichten die in WCF zijn gemaakt of ontvangen, een XML-indeling moeten hebben. Het is aan de kanaalstack om te bepalen hoe de hoofdtekst van het bericht moet worden geïnterpreteerd. Het kan het verzenden als XML, het converteren naar een andere indeling of zelfs helemaal weglaten. Natuurlijk, met de meeste bindingen WCF-leveringen, wordt de berichttekst weergegeven als XML-inhoud in de hoofdtekstsectie van een SOAP-envelop.
Het is belangrijk om te beseffen dat de Message
klasse niet noodzakelijkerwijs een buffer bevat met XML-gegevens die de hoofdtekst vertegenwoordigen. Logisch, Message
bevat een XML-infoset, maar deze infoset kan dynamisch worden samengesteld en bestaat mogelijk nooit fysiek in het geheugen.
Gegevens in de hoofdtekst van het bericht plaatsen
Er is geen uniform mechanisme om gegevens in een berichttekst te plaatsen. De Message klasse heeft een abstracte methode, OnWriteBodyContents(XmlDictionaryWriter)die een XmlDictionaryWriter. Elke subklasse van de Message klasse is verantwoordelijk voor het overschrijven van deze methode en het schrijven van een eigen inhoud. De hoofdtekst van het bericht bevat logisch de XML-infoset die OnWriteBodyContent
produceert. Denk bijvoorbeeld aan de volgende Message
subklasse.
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
Fysiek bevat een AirfareRequestMessage
exemplaar slechts twee tekenreeksen ('fromCity' en 'toCity'). Logisch bevat het bericht echter de volgende XML-infoset:
<airfareRequest>
<from>Tokyo</from>
<to>London</to>
</airfareRequest>
Natuurlijk zou u normaal gesproken op deze manier geen berichten maken, omdat u het serviceframework kunt gebruiken om een bericht zoals de voorgaande te maken op basis van parameters van het bewerkingscontract. Daarnaast bevat de Message klasse statische CreateMessage
methoden die u kunt gebruiken om berichten met algemene inhoudstypen te maken: een leeg bericht, een bericht dat een object bevat dat is geserialiseerd naar XML met de DataContractSerializer, een bericht met een SOAP-fout, een bericht dat XML bevat die wordt vertegenwoordigd door een XmlReader, enzovoort.
Gegevens ophalen uit een berichttekst
U kunt de gegevens die zijn opgeslagen in een berichttekst op twee manieren extraheren:
U kunt de hele berichttekst tegelijk ophalen door de WriteBodyContents(XmlDictionaryWriter) methode aan te roepen en door te geven in een XML-schrijver. De volledige berichttekst wordt naar deze schrijver geschreven. Het tegelijk ontvangen van de hele berichttekst wordt ook wel het schrijven van een bericht genoemd. Het schrijven wordt voornamelijk uitgevoerd door de kanaalstack bij het verzenden van berichten. Een deel van de kanaalstack krijgt meestal toegang tot de hele hoofdtekst van het bericht, coderen en verzenden.
Een andere manier om informatie uit de hoofdtekst van het bericht te halen, is door een XML-lezer aan te roepen GetReaderAtBodyContents() en op te halen. De hoofdtekst van het bericht kan vervolgens sequentieel worden geopend door methoden voor de lezer aan te roepen. Het instellen van de hoofdtekst van het bericht wordt ook wel het lezen van een bericht genoemd. Het lezen van het bericht wordt voornamelijk gebruikt door het serviceframework bij het ontvangen van berichten. Wanneer het DataContractSerializer serviceframework bijvoorbeeld wordt gebruikt, krijgt het serviceframework een XML-lezer over de hoofdtekst en geeft deze door aan de engine voor deserialisatie, die vervolgens begint met het lezen van het berichtelement-per-element en het samenstellen van de bijbehorende objectgrafiek.
Een berichttekst kan slechts eenmaal worden opgehaald. Dit maakt het mogelijk om met alleen-doorstuurstreams te werken. U kunt bijvoorbeeld een OnWriteBodyContents(XmlDictionaryWriter) onderdrukking schrijven die wordt gelezen uit een FileStream en de resultaten als xml-infoset retourneert. U hoeft nooit terug te spoelen naar het begin van het bestand.
De WriteBodyContents
en GetReaderAtBodyContents
methoden controleren eenvoudigweg of de hoofdtekst van het bericht nog nooit eerder is opgehaald en vervolgens aanroepen OnWriteBodyContents
of OnGetReaderAtBodyContents
, respectievelijk.
Berichtgebruik in WCF
De meeste berichten kunnen worden geclassificeerd als uitgaande berichten (berichten die zijn gemaakt door het serviceframework dat door de kanaalstack moet worden verzonden) of inkomende berichten (die afkomstig zijn van de kanaalstack en worden geïnterpreteerd door het serviceframework). Bovendien kan de kanaalstack werken in de gebufferde of streamingmodus. Het serviceframework kan ook een gestreamd of niet-gestreamd programmeermodel beschikbaar maken. Dit leidt tot de gevallen die in de volgende tabel worden vermeld, samen met vereenvoudigde details van hun implementatie.
Berichttype | Hoofdtekstgegevens in bericht | Implementatie van Schrijven (OnWriteBodyContents) | Implementatie van Read (OnGetReaderAtBodyContents) |
---|---|---|---|
Uitgaand, gemaakt op basis van een niet-gestreamd programmeermodel | De gegevens die nodig zijn om het bericht te schrijven (bijvoorbeeld een object en het DataContractSerializer exemplaar dat nodig is om het te serialiseren)* | Aangepaste logica voor het uitschrijven van het bericht op basis van de opgeslagen gegevens (roep bijvoorbeeld WriteObject aan DataContractSerializer of dat de serializer in gebruik is)* |
Aanroepen OnWriteBodyContents , de resultaten bufferen, een XML-lezer retourneren via de buffer |
Uitgaand, gemaakt op basis van gestreamd programmeermodel | De Stream gegevens die moeten worden geschreven* |
Gegevens uit de opgeslagen stream schrijven met behulp van het IStreamProvider mechanisme* | Aanroepen OnWriteBodyContents , de resultaten bufferen, een XML-lezer retourneren via de buffer |
Binnenkomend van streamingkanaalstack | Een Stream object dat de gegevens vertegenwoordigt die via het netwerk binnenkomen met een XmlReader object |
De inhoud uitschrijven van de opgeslagen XmlReader app met WriteNode |
Retourneert de opgeslagen XmlReader |
Binnenkomend van niet-streaming-kanaalstack | Een buffer die hoofdtekstgegevens bevat met er een XmlReader overheen |
Hiermee wordt de inhoud van de opgeslagen XmlReader inhoud weggeschreven met behulp van WriteNode |
Retourneert de opgeslagen lang |
* Deze items worden niet rechtstreeks geïmplementeerd in Message
subklassen, maar in subklassen van de BodyWriter klasse. Zie De berichtklasse gebruiken voor meer informatie over BodyWriterde klasse.
Berichtkoppen
Een bericht kan kopteksten bevatten. Een header bestaat logisch uit een XML-infoset die is gekoppeld aan een naam, een naamruimte en enkele andere eigenschappen. Berichtkoppen worden geopend met behulp van de Headers
eigenschap op Message. Elke koptekst wordt vertegenwoordigd door een MessageHeader klasse. Normaal gesproken worden berichtkoppen toegewezen aan SOAP-berichtkoppen wanneer u een kanaalstack gebruikt die is geconfigureerd voor gebruik met SOAP-berichten.
Het plaatsen van informatie in een berichtkoptekst en het extraheren van informatie uit de berichttekst is vergelijkbaar met het gebruik van de hoofdtekst van het bericht. Het proces is enigszins vereenvoudigd omdat streaming niet wordt ondersteund. Het is mogelijk om meer dan één keer toegang te krijgen tot de inhoud van dezelfde koptekst en kopteksten kunnen in willekeurige volgorde worden geopend, waardoor headers altijd worden gebufferd. Er is geen algemeen mechanisme beschikbaar voor het ophalen van een XML-lezer via een header, maar er is een MessageHeader
subklasse intern voor WCF die een leesbare header met een dergelijke mogelijkheid vertegenwoordigt. Dit type MessageHeader
wordt gemaakt door de kanaalstack wanneer een bericht met aangepaste toepassingsheaders binnenkomt. Hierdoor kan het serviceframework een deserialisatie-engine, zoals de DataContractSerializer, gebruiken om deze headers te interpreteren.
Zie De berichtklasse gebruiken voor meer informatie.
Berichteigenschappen
Een bericht kan eigenschappen bevatten. Een eigenschap is een .NET Framework-object dat is gekoppeld aan een tekenreeksnaam. Eigenschappen worden geopend via de Properties
eigenschap op Message
.
In tegenstelling tot de berichttekst en berichtkoppen (die normaal gesproken worden toegewezen aan de SOAP-hoofdtekst en SOAP-headers), worden berichteigenschappen normaal gesproken niet samen met de berichten verzonden of ontvangen. Berichteigenschappen bestaan voornamelijk als communicatiemechanisme om gegevens over het bericht door te geven tussen de verschillende kanalen in de kanaalstack en tussen de kanaalstack en het servicemodel.
Het HTTP-transportkanaal dat is opgenomen als onderdeel van WCF, kan bijvoorbeeld verschillende HTTP-statuscodes produceren, zoals '404 (Niet gevonden)' en '500 (interne serverfout)' wanneer er antwoorden naar clients worden verzonden. Voordat een antwoordbericht wordt verzonden, wordt gecontroleerd of de Properties
Message
eigenschap 'httpResponse' bevat die een object van het type HttpResponseMessagePropertybevat. Als een dergelijke eigenschap wordt gevonden, wordt de StatusCode eigenschap bekeken en wordt die statuscode gebruikt. Als deze niet wordt gevonden, wordt de standaardcode '200 (OK)' gebruikt.
Zie De berichtklasse gebruiken voor meer informatie.
Het bericht als een Wie le
Tot nu toe hebben we methoden besproken voor toegang tot de verschillende onderdelen van het bericht in isolatie. De Message klasse biedt echter ook methoden om met het hele bericht als geheel te werken. Met de WriteMessage
methode wordt bijvoorbeeld het hele bericht weggeschreven naar een XML-schrijver.
Om dit mogelijk te maken, moet er een toewijzing worden gedefinieerd tussen het hele Message
exemplaar en een XML-infoset. Een dergelijke toewijzing bestaat in feite: WCF gebruikt de SOAP-standaard om deze toewijzing te definiëren. Wanneer een Message
exemplaar wordt weggeschreven als een XML-infoset, is de resulterende Infoset de geldige SOAP-envelop die het bericht bevat. WriteMessage
Voer daarom normaal gesproken de volgende stappen uit:
Schrijf het soap-envelopelement dat wordt geopend.
Schrijf de code voor het openen van het SOAP-headerelement, schrijf alle headers uit en sluit het header-element.
Schrijf de code voor het openen van het SOAP-hoofdtekstelement.
Roep
WriteBodyContents
of een equivalente methode aan om de hoofdtekst op te schrijven.Sluit de hoofdtekst en envelopelementen.
De voorgaande stappen zijn nauw gekoppeld aan de SOAP-standaard. Dit is ingewikkeld omdat er meerdere versies van SOAP bestaan, bijvoorbeeld dat het onmogelijk is om het SOAP-envelopelement correct uit te schrijven zonder de SOAP-versie te kennen die in gebruik is. In sommige gevallen kan het ook wenselijk zijn om deze complexe SOAP-specifieke toewijzing volledig uit te schakelen.
Voor deze doeleinden wordt een Version
eigenschap verstrekt op Message
. Deze kan worden ingesteld op de SOAP-versie die moet worden gebruikt bij het schrijven van het bericht of kan worden ingesteld om None
SOAP-specifieke toewijzingen te voorkomen. Als de Version
eigenschap is ingesteld op None
, fungeren methoden die met het hele bericht werken alsof het bericht alleen bestaat uit de hoofdtekst, WriteMessage
bijvoorbeeld gewoon aanroepen WriteBodyContents
in plaats van de bovenstaande stappen uit te voeren. Er wordt verwacht dat bij binnenkomende berichten Version
automatisch wordt gedetecteerd en correct wordt ingesteld.
De kanaalstack
Kanalen
Zoals eerder vermeld, is de kanaalstack verantwoordelijk voor het converteren van uitgaande Message exemplaren naar een bepaalde actie (zoals het verzenden van pakketten via het netwerk) of het converteren van een bepaalde actie (zoals het ontvangen van netwerkpakketten) naar binnenkomende Message
exemplaren.
De kanaalstack bestaat uit een of meer kanalen die in een reeks zijn gerangschikt. Een uitgaand Message
exemplaar wordt doorgegeven aan het eerste kanaal in de stack (ook wel het bovenste kanaal genoemd), dat het doorgeeft aan het volgende kanaal in stack, enzovoort. Het bericht wordt beëindigd in het laatste kanaal, dat het transportkanaal wordt genoemd. Binnenkomende berichten zijn afkomstig uit het transportkanaal en worden doorgegeven van kanaal naar kanaal omhoog. Vanuit het bovenste kanaal wordt het bericht meestal doorgegeven aan het serviceframework. Hoewel dit het gebruikelijke patroon is voor toepassingsberichten, werken sommige kanalen mogelijk iets anders, bijvoorbeeld, ze kunnen hun eigen infrastructuurberichten verzenden zonder een bericht van een kanaal hierboven door te sturen.
Kanalen kunnen op verschillende manieren op het bericht werken terwijl het door de stack gaat. De meest voorkomende bewerking is het toevoegen van een koptekst aan een uitgaand bericht en het lezen van kopteksten op een binnenkomend bericht. Een kanaal kan bijvoorbeeld de digitale handtekening van een bericht berekenen en toevoegen als koptekst. Een kanaal kan deze digitale handtekeningheader ook controleren op binnenkomende berichten en berichten blokkeren die geen geldige handtekening hebben om de kanaalstack op te stijgen. Kanalen stellen ook vaak berichteigenschappen in of inspecteren. De hoofdtekst van het bericht wordt meestal niet gewijzigd, hoewel dit bijvoorbeeld is toegestaan, kan het WCF-beveiligingskanaal de berichttekst versleutelen.
Transportkanalen en berichtcoderingsprogramma's
Het onderste kanaal in de stack is verantwoordelijk voor het omzetten van een uitgaande Message, zoals gewijzigd door andere kanalen, in een bepaalde actie. Aan de ontvangstzijde is dit het kanaal dat een bepaalde actie converteert naar een Message
ander kanaalproces.
Zoals eerder vermeld, kunnen de acties variëren: het verzenden of ontvangen van netwerkpakketten via verschillende protocollen, het lezen of schrijven van het bericht in een database, of het in de wachtrij plaatsen of het verwijderen van het bericht in een Message Queuing-wachtrij, om maar een paar voorbeelden te geven. Al deze acties hebben één ding gemeen: ze vereisen een transformatie tussen het WCF-exemplaarMessage
en een werkelijke groep bytes die kunnen worden verzonden, ontvangen, gelezen, geschreven, in de wachtrij geplaatst of verwijderd. Het proces van het converteren van een Message
naar een groep bytes wordt codering genoemd en het omgekeerde proces voor het maken van een Message
van een groep bytes wordt decodering genoemd.
De meeste transportkanalen maken gebruik van onderdelen die berichtcoderingsprogramma's worden genoemd om het coderings- en decoderingswerk te voltooien. Een berichtcoderingsprogramma is een subklasse van de MessageEncoder klasse. MessageEncoder
bevat verschillende ReadMessage
en WriteMessage
methodeoverbelastingen om te converteren tussen Message
en groepen bytes.
Aan de verzendzijde geeft een bufferend transportkanaal het Message
object door dat het heeft ontvangen van een kanaal erboven.WriteMessage
Er wordt een matrix van bytes geretourneerd die vervolgens wordt gebruikt om de actie uit te voeren (zoals het verpakken van deze bytes als geldige TCP-pakketten en verzenden naar de juiste bestemming). Een streamingtransportkanaal maakt eerst een Stream
(bijvoorbeeld via de uitgaande TCP-verbinding) en geeft vervolgens zowel de Stream
als de Message
gegevens die moeten worden verzonden door naar de juiste WriteMessage
overbelasting, waarmee het bericht wordt weggeschreven.
Aan de ontvangstzijde extraheert een bufferend transportkanaal binnenkomende bytes (bijvoorbeeld van binnenkomende TCP-pakketten) in een matrix en aanroepen ReadMessage
om een Message
object op te halen dat kan worden doorgegeven aan de kanaalstack. Een streamingtransportkanaal maakt een Stream
object (bijvoorbeeld een netwerkstroom via de binnenkomende TCP-verbinding) en geeft dit door om een Message
object terug te ReadMessage
krijgen.
De scheiding tussen de transportkanalen en de berichtcoderingsprogramma is niet verplicht; het is mogelijk om een transportkanaal te schrijven dat geen berichtcoderingsprogramma gebruikt. Het voordeel van deze scheiding is echter gemak van samenstelling. Zolang een transportkanaal alleen de basis MessageEncodergebruikt, kan het werken met een WCF- of externe berichtcoderingsprogramma. Op dezelfde manier kan dezelfde encoder normaal gesproken in elk transportkanaal worden gebruikt.
Berichtcoderingsbewerking
Als u de typische werking van een encoder wilt beschrijven, is het handig om rekening te houden met de volgende vier gevallen.
Operation | Opmerking |
---|---|
Codering, gebufferd | In de buffermodus maakt de encoder normaal gesproken een buffer van variabele grootte en maakt er vervolgens een XML-schrijver overheen. Vervolgens wordt WriteMessage(XmlWriter) het bericht aangeroepen dat wordt gecodeerd, waarmee de headers worden weggeschreven en vervolgens de hoofdtekst wordt gebruikt WriteBodyContents(XmlDictionaryWriter), zoals uitgelegd in de voorgaande sectie over Message in dit onderwerp. De inhoud van de buffer (weergegeven als een matrix van bytes) wordt vervolgens geretourneerd voor het te gebruiken transportkanaal. |
Codering, gestreamd | In de gestreamde modus is de bewerking vergelijkbaar met de bovenstaande, maar eenvoudiger. Er is geen buffer nodig. Een XML-schrijver wordt normaal gesproken via de stream gemaakt en WriteMessage(XmlWriter) wordt aangeroepen Message om deze naar deze schrijver te schrijven. |
Decodering, Gebufferd | Bij het decoderen in de buffermodus wordt normaal gesproken een speciale Message subklasse gemaakt die de gebufferde gegevens bevat. De kopteksten van het bericht worden gelezen en er wordt een XML-lezer op de hoofdtekst van het bericht gemaakt. Dit is de lezer die wordt geretourneerd met GetReaderAtBodyContents(). |
Decodering, Gestreamd | Bij het decoderen in de gestreamde modus wordt normaal gesproken een speciale berichtsubklasse gemaakt. De stream is net genoeg geavanceerd om alle kopteksten te lezen en in de berichttekst te plaatsen. Vervolgens wordt er een XML-lezer gemaakt via de stream. Dit is de lezer die wordt geretourneerd met GetReaderAtBodyContents(). |
Encoders kunnen ook andere functies uitvoeren. De encoders kunnen bijvoorbeeld XML-lezers en schrijvers groepeert. Het is duur om elke keer een nieuwe XML-lezer of schrijver te maken wanneer er een nodig is. Daarom onderhouden encoders normaal gesproken een groep lezers en een groep schrijvers van configureerbare grootte. In de beschrijvingen van de coderingsprogrammabewerking die eerder is beschreven, betekent dit wanneer de woordgroep 'een XML-lezer/schrijver maken' wordt gebruikt, normaal gesproken 'een van de pool nemen of er een maken als deze niet beschikbaar is'. De encoder (en de Message
subklassen die worden gemaakt tijdens het decoderen) bevatten logica om lezers en schrijvers terug te keren naar de pools zodra ze niet meer nodig zijn (bijvoorbeeld wanneer de Message
code wordt gesloten).
WCF biedt drie berichtcoderingsprogramma's, hoewel het mogelijk is om extra aangepaste typen te maken. De opgegeven typen zijn Text, Binary en Message Transmission Optimization Mechanism (MTOM). Deze worden uitgebreid beschreven in Het kiezen van een berichtcoderingsprogramma.
De IStreamProvider-interface
Bij het schrijven van een uitgaand bericht dat een gestreamde hoofdtekst naar een XML-schrijver bevat, Message wordt een reeks aanroepen gebruikt die vergelijkbaar is met de volgende in de OnWriteBodyContents(XmlDictionaryWriter) implementatie:
Schrijf alle benodigde informatie voorafgaand aan de stream (bijvoorbeeld het openen van de XML-tag).
Schrijf de stream.
Schrijf alle informatie na de stream (bijvoorbeeld de afsluitende XML-tag).
Dit werkt goed met coderingen die vergelijkbaar zijn met de tekstuele XML-codering. Sommige coderingen plaatsen echter geen XML-gegevenssetgegevens (bijvoorbeeld tags voor het starten en beëindigen van XML-elementen) samen met de gegevens in elementen. In de MTOM-codering wordt het bericht bijvoorbeeld gesplitst in meerdere delen. Het ene deel bevat de XML-infoset, die mogelijk verwijzingen naar andere onderdelen voor de inhoud van het element bevat. De XML-infoset is normaal gesproken klein in vergelijking met de gestreamde inhoud, dus het is logisch om de infoset te bufferen, uit te schrijven en vervolgens de inhoud op een gestreamde manier te schrijven. Dit betekent dat de stream op het moment dat de tag voor het afsluitende element is geschreven, nog niet is weggeschreven.
Hiervoor wordt de IStreamProvider interface gebruikt. De interface heeft een GetStream() methode die de stroom retourneert die moet worden geschreven. De juiste manier om een gestreamde berichttekst uit OnWriteBodyContents(XmlDictionaryWriter) te schrijven, is als volgt:
Schrijf alle benodigde informatie voorafgaand aan de stream (bijvoorbeeld het openen van de XML-tag).
Roep de
WriteValue
overbelasting aan die XmlDictionaryWriter een IStreamProvider, met eenIStreamProvider
implementatie die de stroom retourneert die moet worden geschreven.Schrijf alle informatie na de stream (bijvoorbeeld de afsluitende XML-tag).
Met deze methode heeft de XML-schrijver de keuze wanneer de gestreamde gegevens moeten worden aangeroepen GetStream() en weggeschreven. De tekstuele en binaire XML-schrijvers noemen deze bijvoorbeeld onmiddellijk en schrijven de gestreamde inhoud tussen de begin- en eindtags uit. De MTOM-schrijver kan besluiten later aan te roepen GetStream() wanneer het gereed is om het juiste deel van het bericht te schrijven.
Gegevens weergeven in het Service Framework
Zoals vermeld in de sectie 'Basisarchitectuur' van dit onderwerp, is het serviceframework het onderdeel van WCF dat onder andere verantwoordelijk is voor het converteren tussen een gebruiksvriendelijk programmeermodel voor berichtgegevens en werkelijke Message
exemplaren. Normaal gesproken wordt een berichtuitwisseling weergegeven in het serviceframework als een .NET Framework-methode die is gemarkeerd met het OperationContractAttribute kenmerk. De methode kan enkele parameters in beslag nemen en kan een retourwaarde of outparameters (of beide) retourneren. Aan de servicezijde vertegenwoordigen de invoerparameters het binnenkomende bericht en de retourwaarde en outparameters vertegenwoordigen het uitgaande bericht. Aan de clientzijde is het omgekeerde waar. Het programmeermodel voor het beschrijven van berichten met behulp van parameters en de retourwaarde wordt gedetailleerd beschreven in Het opgeven van gegevensoverdracht in servicecontracten. In deze sectie vindt u echter een kort overzicht.
Programmeermodellen
Het WCF-serviceframework ondersteunt vijf verschillende programmeermodellen voor het beschrijven van berichten:
1. Het lege bericht
Dit is het eenvoudigste geval. Als u een leeg binnenkomend bericht wilt beschrijven, gebruikt u geen invoerparameters.
[OperationContract]
int GetCurrentTemperature();
<OperationContract()> Function GetCurrentTemperature() As Integer
Als u een leeg uitgaand bericht wilt beschrijven, gebruikt u een ongeldige retourwaarde en gebruikt u geen outparameters:
[OperationContract]
void SetDesiredTemperature(int t);
<OperationContract()> Sub SetDesiredTemperature(ByVal t As Integer)
Houd er rekening mee dat dit verschilt van een eenrichtingsbewerkingscontract:
[OperationContract(IsOneWay = true)]
void SetLightbulb(bool isOn);
<OperationContract(IsOneWay:=True)> Sub SetLightbulb(ByVal isOn As Boolean)
In het SetDesiredTemperature
voorbeeld wordt een tweerichtingspatroon voor het uitwisselen van berichten beschreven. Er wordt een bericht geretourneerd vanuit de bewerking, maar het is leeg. Het is mogelijk om een fout van de bewerking te retourneren. In het voorbeeld 'Gloeilamp instellen' is het patroon berichtuitwisseling in één richting, dus er is geen uitgaand bericht om te beschrijven. De service kan in dit geval geen status aan de client doorgeven.
2. De berichtklasse rechtstreeks gebruiken
Het is mogelijk om de klasse (of een van de Message subklassen) rechtstreeks in een bewerkingscontract te gebruiken. In dit geval geeft het serviceframework alleen de Message
bewerking door aan de kanaalstack en vice versa, zonder verdere verwerking.
Er zijn twee hoofdgebruiksvoorbeelden voor het rechtstreeks gebruiken Message
. U kunt dit gebruiken voor geavanceerde scenario's wanneer geen van de andere programmeermodellen voldoende flexibiliteit biedt om uw boodschap te beschrijven. U kunt bijvoorbeeld bestanden op schijf gebruiken om een bericht te beschrijven, waarbij de eigenschappen van het bestand berichtkoppen worden en de inhoud van het bestand de hoofdtekst van het bericht wordt. Vervolgens kunt u iets vergelijkbaars met het volgende maken.
public class FileMessage : Message
// Code not shown.
Public Class FileMessage
Inherits Message
' Code not shown.
Het tweede veelvoorkomende gebruik voor Message
een bewerkingscontract is wanneer een service niet om de specifieke inhoud van het bericht geeft en op het bericht reageert als in een zwart vak. U hebt bijvoorbeeld een service waarmee berichten worden doorgestuurd naar meerdere andere geadresseerden. Het contract kan als volgt worden geschreven.
[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
De regel Action="*" schakelt het verzenden van berichten effectief uit en zorgt ervoor dat alle berichten die naar het IForwardingService
contract worden verzonden, hun weg naar de ForwardMessage
bewerking maken. (Normaal gesproken onderzoekt de dispatcher de header Actie van het bericht om te bepalen voor welke bewerking deze is bedoeld. Action="*" betekent "alle mogelijke waarden van de actieheader".) De combinatie van Action="*" en het gebruik van Bericht als een parameter wordt het "universele contract" genoemd omdat het alle mogelijke berichten kan ontvangen. Als u alle mogelijke berichten wilt verzenden, gebruikt u Bericht als retourwaarde en stelt u in op ReplyAction
*. Hierdoor voorkomt u dat het serviceframework een eigen actieheader toevoegt, zodat u deze header kunt beheren met behulp van het Message
object dat u retourneert.
3. Berichtcontracten
WCF biedt een declaratief programmeermodel voor het beschrijven van berichten, zogenaamde berichtcontracten. Dit model wordt uitgebreid beschreven in Het gebruik van berichtcontracten. In wezen wordt het hele bericht vertegenwoordigd door één .NET Framework-type dat gebruikmaakt van kenmerken zoals de MessageBodyMemberAttribute en MessageHeaderAttribute om te beschrijven welke onderdelen van de berichtcontractklasse moeten worden toegewezen aan welk deel van het bericht.
Berichtcontracten bieden veel controle over de resulterende Message
exemplaren (hoewel duidelijk niet zoveel controle als het rechtstreeks gebruiken van de Message
klasse). Berichtteksten bestaan bijvoorbeeld vaak uit meerdere stukjes informatie, die elk worden vertegenwoordigd door een eigen XML-element. Deze elementen kunnen rechtstreeks in de hoofdtekst (bare-modus ) voorkomen of kunnen worden verpakt in een omvattend XML-element. Met behulp van het berichtcontractprogrammeringsmodel kunt u de bare-versus-wrapped beslissing nemen en de naam van de wrapper-naam en naamruimte beheren.
In het volgende codevoorbeeld van een berichtcontract ziet u deze functies.
[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
Items die zijn gemarkeerd om te worden geserialiseerd (met de MessageBodyMemberAttribute, MessageHeaderAttributeof andere gerelateerde kenmerken) moeten serialiseren om deel te nemen aan een berichtcontract. Zie de sectie Serialisatie verderop in dit onderwerp voor meer informatie.
4. Parameters
Een ontwikkelaar die een bewerking wil beschrijven die op meerdere gegevens werkt, heeft vaak niet de mate van controle nodig die berichtencontracten bieden. Wanneer u bijvoorbeeld nieuwe services maakt, wilt u meestal niet de bare-versus-wrapped beslissing nemen en beslissen over de naam van het wrapper-element. Voor het nemen van deze beslissingen is vaak uitgebreide kennis van webservices en SOAP vereist.
Het WCF-serviceframework kan automatisch de beste en meest interoperabele SOAP-weergave kiezen voor het verzenden of ontvangen van meerdere gerelateerde gegevens, zonder dat deze keuzes voor de gebruiker worden afgedwongen. Dit wordt bereikt door deze stukjes informatie te beschrijven als parameters of retourwaarden van een bewerkingscontract. Denk bijvoorbeeld aan het volgende bewerkingscontract.
[OperationContract]
void SubmitOrder(string customerID, string item, int quantity);
<OperationContract()> _
Sub SubmitOrder( _
ByVal customerID As String, _
ByVal item As String, _
ByVal quantity As Integer)
Het serviceframework besluit automatisch alle drie de stukjes informatie (customerID
, item
en quantity
) in de berichttekst te plaatsen en deze in te pakken in een wrapper-element met de naam SubmitOrderRequest
.
Het beschrijven van de informatie die moet worden verzonden of ontvangen als een eenvoudige lijst met bewerkingscontractparameters is de aanbevolen aanpak, tenzij er speciale redenen zijn om over te stappen op het complexere berichtcontract of Message
op basis van programmeermodellen.
5. Stream
Het gebruik Stream
van of een van de subklassen in een bewerkingscontract of als enige berichttekstgedeelte in een berichtcontract kan worden beschouwd als een afzonderlijk programmeermodel van de hierboven beschreven modellen. Het gebruik op Stream
deze manier is de enige manier om te garanderen dat uw contract bruikbaar is op een gestreamde manier, kort van het schrijven van uw eigen streaming-compatibele Message
subklasse. Zie Large Data en Streaming voor meer informatie.
Wanneer Stream
of een van de subklassen op deze manier wordt gebruikt, wordt de serializer niet aangeroepen. Voor uitgaande berichten wordt een speciale streamingsubklasse Message
gemaakt en wordt de stream geschreven zoals beschreven in de sectie over de IStreamProvider interface. Voor binnenkomende berichten maakt het serviceframework een Stream
subklasse voor het binnenkomende bericht en biedt het deze aan de bewerking.
Beperkingen voor programmeermodellen
De hierboven beschreven programmeermodellen kunnen niet willekeurig worden gecombineerd. Als een bewerking bijvoorbeeld een berichtcontracttype accepteert, moet het berichtcontract de enige invoerparameter zijn. Bovendien moet de bewerking vervolgens een leeg bericht retourneren (retourtype van ongeldigheid) of een ander berichtcontract. Deze beperkingen voor programmeermodellen worden beschreven in de onderwerpen voor elk specifiek programmeermodel: Message Contracts gebruiken, de berichtklasse gebruiken en grote gegevens en streaming.
Berichtindelingen
De hierboven beschreven programmeermodellen worden ondersteund door het aansluiten van onderdelen die berichtindelingen worden genoemd in het serviceframework. Berichtindelingen zijn typen die respectievelijk de IClientMessageFormatter of IDispatchMessageFormatter interface implementeren, of beide, voor gebruik in clients en SERVICE WCF-clients.
Berichtindelingen worden normaal gesproken aangesloten op gedrag. De invoegtoepassingen van de DataContractSerializerOperationBehavior berichtindeling voor het gegevenscontract zijn bijvoorbeeld opgenomen. Dit wordt gedaan aan de servicezijde door in te stellen Formatter op de juiste formatter in de ApplyDispatchBehavior(OperationDescription, DispatchOperation) methode of aan de clientzijde door de juiste formatter in de ApplyClientBehavior(OperationDescription, ClientOperation) methode in te stellenFormatter.
De volgende tabellen bevatten de methoden die een berichtindeling kan implementeren.
Interface | Wijze | Actie |
---|---|---|
IDispatchMessageFormatter | DeserializeRequest(Message, Object[]) | Converteert een binnenkomende Message naar bewerkingsparameters |
IDispatchMessageFormatter | SerializeReply(MessageVersion, Object[], Object) | Hiermee maakt u een uitgaande Message waarde voor de retourwaarde/outparameters van de bewerking |
IClientMessageFormatter | SerializeRequest(MessageVersion, Object[]) | Hiermee maakt u een uitgaande Message bewerkingsparameters |
IClientMessageFormatter | DeserializeReply(Message, Object[]) | Converteert een binnenkomende Message waarde/uitgaande parameters naar een retourwaarde |
Serialization
Wanneer u berichtcontracten of -parameters gebruikt om de inhoud van berichten te beschrijven, moet u serialisatie gebruiken om te converteren tussen .NET Framework-typen en XML-infosetweergave. Serialisatie wordt gebruikt op andere plaatsen in WCF, bijvoorbeeld een Message algemene GetBody methode die u kunt gebruiken om de hele hoofdtekst van het bericht te lezen die in een object is gedeserialiseerd.
WCF ondersteunt twee serialisatietechnologieën 'out-of-the-box' voor het serialiseren en deserialiseren van parameters en berichtonderdelen: de DataContractSerializer en de XmlSerializer
. Daarnaast kunt u aangepaste serializers schrijven. Andere onderdelen van WCF (zoals de algemene GetBody
methode of SOAP-foutserialisatie) kunnen echter worden beperkt tot alleen de XmlObjectSerializer subklassen (DataContractSerializer en NetDataContractSerializer, maar niet de XmlSerializer), of kunnen zelfs in code zijn vastgelegd om alleen de DataContractSerializer.
Dit XmlSerializer
is de serialisatie-engine die wordt gebruikt in ASP.NET webservices. Dit DataContractSerializer
is de nieuwe serialisatie-engine die het nieuwe programmeermodel van het gegevenscontract begrijpt. DataContractSerializer
is de standaardkeuze en de keuze om de XmlSerializer
te gebruiken kan per bewerking worden gemaakt met behulp van het DataContractFormatAttribute kenmerk.
DataContractSerializerOperationBehavior en XmlSerializerOperationBehavior zijn de werkingsgedragen die verantwoordelijk zijn voor het aansluiten van de berichtopmaakters voor respectievelijk de DataContractSerializer
en de XmlSerializer
. Het DataContractSerializerOperationBehavior gedrag kan in feite worden uitgevoerd met elke serialisatiefunctie die is afgeleid van XmlObjectSerializer, inclusief de NetDataContractSerializer (beschreven in detail beschreven in Het gebruik van zelfstandige serialisatie). Het gedrag roept een van de CreateSerializer
virtuele methode-overbelastingen aan om de serializer te verkrijgen. Als u een andere serializer wilt aansluiten, maakt u een nieuwe DataContractSerializerOperationBehavior subklasse en overschrijft u beide CreateSerializer
overbelastingen.