Omówienie architektury transferu danych

Program Windows Communication Foundation (WCF) może być uważany za infrastrukturę obsługi komunikatów. Może odbierać komunikaty, przetwarzać je i wysyłać do kodu użytkownika w celu wykonania dalszych działań lub tworzyć komunikaty z danych podanych przez kod użytkownika i dostarczać je do miejsca docelowego. W tym temacie, przeznaczonym dla zaawansowanych deweloperów, opisano architekturę obsługi komunikatów i zawartych danych. Aby uzyskać prostszy, zorientowany na zadania widok sposobu wysyłania i odbierania danych, zobacz Określanie transferu danych w kontraktach usług.

Uwaga

W tym temacie omówiono szczegóły implementacji programu WCF, które nie są widoczne, sprawdzając model obiektów WCF. Dwa słowa ostrożności są w porządku w odniesieniu do udokumentowanych szczegółów implementacji. Najpierw opisy są uproszczone; rzeczywista implementacja może być bardziej złożona ze względu na optymalizacje lub inne przyczyny. Po drugie, nigdy nie należy polegać na konkretnych szczegółach implementacji, nawet udokumentowanych, ponieważ mogą one ulec zmianie bez powiadomienia z wersji na wersję, a nawet w wydaniu obsługi.

Architektura podstawowa

Podstawową funkcją obsługi komunikatów WCF jest Message klasa opisana szczegółowo w temacie Using the Message Class (Używanie klasy komunikatów). Składniki czasu wykonywania WCF można podzielić na dwie główne części: stos kanału i strukturę usług, z Message klasą będącą punktem połączenia.

Stos kanału jest odpowiedzialny za konwersję między prawidłowym Message wystąpieniem a akcją odpowiadającą wysyłaniu lub odbieraniu danych komunikatów. Po stronie wysyłania stos kanału przyjmuje prawidłowe Message wystąpienie, a po pewnym przetworzeniu wykonuje pewną akcję, która logicznie odpowiada wysyłaniu komunikatu. Akcja może dotyczyć wysyłania pakietów TCP lub HTTP, kolejkowania komunikatu w kolejce komunikatów, zapisywania komunikatu w bazie danych, zapisywania go w udziale plików lub dowolnej innej akcji w zależności od implementacji. Najczęstszą akcją jest wysłanie komunikatu za pośrednictwem protokołu sieciowego. Po stronie odbierającej dzieje się odwrotnie — wykryto akcję (która może być przychodzącymi pakietami TCP lub HTTP lub dowolną inną akcją), a po przetworzeniu stos kanału konwertuje tę akcję na prawidłowe Message wystąpienie.

Usługi WCF można używać bezpośrednio przy użyciu Message klasy i stosu kanału. Jednak takie działanie jest trudne i czasochłonne. Message Ponadto obiekt nie obsługuje metadanych, więc nie można wygenerować silnie typicznie typiowanych klientów WCF, jeśli w ten sposób używasz usługi WCF.

W związku z tym program WCF zawiera platformę usług, która zapewnia łatwy w użyciu model programowania, którego można użyć do konstruowania i odbierania Message obiektów. Struktura usług mapuje usługi na typy .NET Framework za pomocą pojęcia kontraktów usług i wysyła komunikaty do operacji użytkownika, które są po prostu metodami programu .NET Framework oznaczonymi atrybutem OperationContractAttribute (aby uzyskać więcej informacji, zobacz Projektowanie kontraktów usług). Metody te mogą mieć parametry i zwracane wartości. Po stronie usługi platforma usług konwertuje wystąpienia przychodzące Message na parametry i konwertuje wartości zwracane na wystąpienia wychodzące Message . Po stronie klienta działa odwrotnie. Rozważmy na przykład poniższą operację FindAirfare .

[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

Załóżmy, że FindAirfare jest wywoływana na kliencie. Struktura usług na kliencie konwertuje FromCity parametry i ToCity na wystąpienie wychodzące Message i przekazuje je do stosu kanału do wysłania.

Po stronie usługi po Message nadejściu wystąpienia ze stosu kanału platforma usługi wyodrębnia odpowiednie dane z komunikatu, aby wypełnić FromCity parametry i, ToCity a następnie wywołuje metodę po stronie FindAirfare usługi. Gdy metoda zwróci metodę, platforma usługi przyjmuje zwracaną wartość całkowitą i IsDirectFlight parametr wyjściowy i tworzy Message wystąpienie obiektu zawierające te informacje. Następnie przekazuje Message wystąpienie do stosu kanału, który ma zostać odesłany do klienta.

Po stronie klienta wystąpienie zawierające Message komunikat odpowiedzi pojawia się ze stosu kanału. Struktura usługi wyodrębnia wartość zwracaną i IsDirectFlight wartość i zwraca je do obiektu wywołującego klienta.

Message, klasa

Klasa Message ma być abstrakcyjną reprezentacją komunikatu, ale jego projekt jest silnie powiązany z komunikatem SOAP. Element Message zawiera trzy główne informacje: treść komunikatu, nagłówki komunikatów i właściwości komunikatu.

Treść wiadomości

Treść komunikatu jest przeznaczona do reprezentowania rzeczywistego ładunku danych komunikatu. Treść komunikatu jest zawsze reprezentowana jako zestaw informacji XML. Nie oznacza to, że wszystkie komunikaty utworzone lub odebrane w programie WCF muszą mieć format XML. Do stosu kanału należy zdecydować, jak interpretować treść komunikatu. Może emitować go jako XML, konwertować go na inny format, a nawet całkowicie pominąć. Oczywiście w większości powiązań dostarcza WCF treść komunikatu jest reprezentowana jako zawartość XML w sekcji treści koperty protokołu SOAP.

Należy pamiętać, że Message klasa nie musi zawierać buforu zawierającego dane XML reprezentujące treść. Logicznie zawiera zestaw informacji XML, Message ale ten zestaw informacji może być dynamicznie skonstruowany i nigdy nie może istnieć fizycznie w pamięci.

Umieszczanie danych w treści komunikatu

Nie ma jednolitego mechanizmu umieszczania danych w treści komunikatu. Klasa Message ma metodę abstrakcyjną , OnWriteBodyContents(XmlDictionaryWriter)która przyjmuje element XmlDictionaryWriter. Każda podklasa Message klasy jest odpowiedzialna za zastąpienie tej metody i zapisanie własnej zawartości. Treść komunikatu logicznie zawiera zestaw informacji XML, który OnWriteBodyContent generuje. Rozważmy na przykład następującą Message podklasę.

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

Fizycznie AirfareRequestMessage wystąpienie zawiera tylko dwa ciągi ("fromCity" i "toCity"). Jednak logicznie komunikat zawiera następujący zestaw informacji XML:

<airfareRequest>  
    <from>Tokyo</from>  
    <to>London</to>  
</airfareRequest>  

Oczywiście zwykle nie można tworzyć komunikatów w ten sposób, ponieważ można użyć platformy usług do utworzenia komunikatu takiego jak poprzedni z parametrów kontraktu operacji. Ponadto klasa ma metody statyczneCreateMessage, Message których można użyć do tworzenia komunikatów o typowych typach zawartości: pustego komunikatu, komunikatu zawierającego obiekt serializowany do kodu XML z DataContractSerializerkomunikatem zawierającym błąd protokołu SOAP, komunikat zawierający kod XML reprezentowany przez XmlReaderelement i tak dalej.

Pobieranie danych z treści komunikatu

Dane przechowywane w treści komunikatu można wyodrębnić na dwa główne sposoby:

  • Całą treść komunikatu można uzyskać jednocześnie, wywołując metodę WriteBodyContents(XmlDictionaryWriter) i przekazując składnik zapisywania XML. Kompletna treść wiadomości jest zapisywana w tym zapisie. Pobieranie całej treści wiadomości jednocześnie jest również nazywane pisaniem wiadomości. Pisanie odbywa się głównie przez stos kanału podczas wysyłania komunikatów — część stosu kanału zwykle uzyskuje dostęp do całej treści komunikatu, koduje je i wysyła.

  • Innym sposobem na wyprowadzenie informacji z treści wiadomości jest wywołanie GetReaderAtBodyContents() i pobranie czytnika XML. Następnie można uzyskać dostęp do treści wiadomości sekwencyjnie zgodnie z potrzebami, wywołując metody na czytniku. Pobieranie treści wiadomości po kawałku jest również nazywane czytaniem wiadomości. Odczytywanie komunikatu jest używane głównie przez platformę usług podczas odbierania komunikatów. Na przykład gdy DataContractSerializer obiekt jest w użyciu, struktura usługi uzyska czytnik XML nad treścią i przekaże go do aparatu deserializacji, który następnie rozpocznie odczytywanie elementu komunikatu po elemenie i konstruowanie odpowiedniego grafu obiektu.

Treść komunikatu można pobrać tylko raz. Dzięki temu można pracować ze strumieniami tylko do przodu. Na przykład można napisać OnWriteBodyContents(XmlDictionaryWriter) przesłonięcia, które odczytuje z elementu FileStream i zwraca wyniki jako zestaw informacji XML. Nigdy nie trzeba będzie "przewijać" na początku pliku.

Metody WriteBodyContents i GetReaderAtBodyContents po prostu sprawdzają, czy treść komunikatu nigdy wcześniej nie została pobrana, a następnie wywołaj OnWriteBodyContents odpowiednio metodę lub OnGetReaderAtBodyContents.

Użycie komunikatów w programie WCF

Większość komunikatów można sklasyfikować jako wychodzące (te, które są tworzone przez strukturę usług do wysłania przez stos kanału) lub przychodzące (te, które docierają ze stosu kanału i są interpretowane przez platformę usług). Ponadto stos kanału może działać w trybie buforowania lub przesyłania strumieniowego. Platforma usług może również uwidaczniać model programowania przesyłanego strumieniowo lub bez transmisji strumieniowej. Prowadzi to do przypadków wymienionych w poniższej tabeli wraz z uproszczonymi szczegółami ich implementacji.

Typ wiadomości Dane treści w wiadomości Implementacja zapisu (OnWriteBodyContents) Odczyt (OnGetReaderAtBodyContents) Implementacja
Wychodzące, utworzone na podstawie niespływowego modelu programowania Dane potrzebne do zapisania komunikatu (na przykład obiektu i DataContractSerializer wystąpienia potrzebnego do serializacji)* Niestandardowa logika zapisywania komunikatu na podstawie przechowywanych danych (na przykład wywołaj WriteObject metodę , DataContractSerializer jeśli jest to serializator używany)* Wywołaj OnWriteBodyContentsmetodę , buforuj wyniki, zwróć czytnik XML za pośrednictwem buforu
Wychodzące, utworzone na podstawie modelu programowania przesyłanego strumieniowo Element Stream z danymi do zapisania* Zapisywanie danych ze strumienia przechowywanego IStreamProvider przy użyciu mechanizmu* Wywołaj OnWriteBodyContentsmetodę , buforuj wyniki, zwróć czytnik XML za pośrednictwem buforu
Przychodzące ze stosu kanału przesyłania strumieniowego Stream Obiekt reprezentujący dane przychodzące za pośrednictwem sieci za XmlReader pośrednictwem obiektu Zapisywanie zawartości przechowywanej XmlReader przy użyciu polecenia WriteNode Zwraca przechowywany XmlReader
Przychodzące ze stosu kanału innego niż strumieniowa Bufor, który zawiera dane treści, za jego pośrednictwem XmlReader Zapisuje zawartość z przechowywanego XmlReader przy użyciu polecenia WriteNode Zwraca przechowywany lang

* Te elementy nie są implementowane bezpośrednio w Message podklasach, ale w podklasach BodyWriter klasy. Aby uzyskać więcej informacji na temat klasy BodyWriter, zobacz Using the Message Class (Używanie klasy komunikatów).

Nagłówki komunikatów

Komunikat może zawierać nagłówki. Nagłówek logicznie składa się z zestawu informacji XML skojarzonego z nazwą, przestrzenią nazw i kilkoma innymi właściwościami. Dostęp do nagłówków komunikatów jest uzyskiwany przy użyciu Headers właściwości w pliku Message. Każdy nagłówek jest reprezentowany przez klasę MessageHeader . Zwykle nagłówki komunikatów są mapowane na nagłówki komunikatów PROTOKOŁU SOAP podczas korzystania ze stosu kanału skonfigurowanego do pracy z komunikatami PROTOKOŁU SOAP.

Umieszczenie informacji w nagłówku wiadomości i wyodrębnienie z niego informacji jest podobne do użycia treści komunikatu. Proces jest nieco uproszczony, ponieważ przesyłanie strumieniowe nie jest obsługiwane. Istnieje możliwość uzyskania dostępu do zawartości tego samego nagłówka więcej niż raz, a dostęp do nagłówków można uzyskać w dowolnej kolejności, wymuszając buforowanie nagłówków. Brak dostępnego mechanizmu ogólnego przeznaczenia w celu pobrania czytnika XML za pośrednictwem nagłówka, ale istnieje MessageHeader wewnętrzna podklasa WCF, która reprezentuje czytelny nagłówek z taką możliwością. Ten typ jest tworzony przez stos kanału MessageHeader , gdy pojawia się komunikat z niestandardowymi nagłówkami aplikacji. Dzięki temu platforma usług może użyć aparatu deserializacji, takiego jak DataContractSerializer, w celu zinterpretowania tych nagłówków.

Aby uzyskać więcej informacji, zobacz Using the Message Class (Używanie klasy komunikatów).

Właściwości komunikatu

Komunikat może zawierać właściwości. Właściwość to dowolny obiekt .NET Framework skojarzony z nazwą ciągu. Dostęp do właściwości jest uzyskiwany za pośrednictwem Properties właściwości w pliku Message.

W przeciwieństwie do treści wiadomości i nagłówków komunikatów (które zwykle są mapowane na treść protokołu SOAP i nagłówki protokołu SOAP), właściwości komunikatów zwykle nie są wysyłane ani odbierane wraz z komunikatami. Właściwości komunikatu istnieją przede wszystkim jako mechanizm komunikacji do przekazywania danych dotyczących komunikatu między różnymi kanałami w stosie kanału i między stosem kanału a modelem usługi.

Na przykład kanał transportu HTTP dołączony jako część programu WCF może tworzyć różne kody stanu HTTP, takie jak "404 (nie znaleziono)" i "500 (wewnętrzny błąd serwera)," podczas wysyłania odpowiedzi do klientów. Przed wysłaniem wiadomości odpowiedzi sprawdza, czy PropertiesMessage właściwość zawiera właściwość o nazwie "httpResponse", która zawiera obiekt typu HttpResponseMessageProperty. Jeśli taka właściwość zostanie znaleziona, przyjrzy się właściwości i użyje StatusCode tego kodu stanu. Jeśli nie zostanie znaleziony, zostanie użyty domyślny kod "200 (OK)".

Aby uzyskać więcej informacji, zobacz Using the Message Class (Używanie klasy komunikatów).

Komunikat jako KtoTo le

Do tej pory omówiliśmy metody uzyskiwania dostępu do różnych części wiadomości w izolacji. Message Jednak klasa udostępnia również metody pracy z całym komunikatem jako całością. Na przykład WriteMessage metoda zapisuje cały komunikat w składniku zapisywania XML.

Aby można było to możliwe, należy zdefiniować mapowanie między całym Message wystąpieniem a zestawem informacji XML. Takie mapowanie istnieje w rzeczywistości: WCF używa standardu SOAP do zdefiniowania tego mapowania. Message Gdy wystąpienie jest zapisywane jako zestaw informacji XML, wynikowy zestaw informacji jest prawidłową kopertą protokołu SOAP zawierającą komunikat. WriteMessage W związku z tym zwykle należy wykonać następujące czynności:

  1. Zapisz tag otwierający element koperty PROTOKOŁU SOAP.

  2. Napisz tag otwierający element nagłówka SOAP, zapisz wszystkie nagłówki i zamknij element nagłówka.

  3. Napisz tag otwierający element treści protokołu SOAP.

  4. Wywołaj WriteBodyContents metodę lub równoważną metodę w celu zapisania treści.

  5. Zamknij elementy treści i koperty.

Powyższe kroki są ściśle powiązane ze standardem SOAP. Jest to skomplikowane przez fakt, że istnieje wiele wersji protokołu SOAP, na przykład nie można poprawnie zapisać elementu koperty PROTOKOŁU SOAP bez znajomości używanej wersji protokołu SOAP. Ponadto w niektórych przypadkach może być pożądane całkowite wyłączenie tego złożonego mapowania specyficznego dla protokołu SOAP.

W tym celu Version właściwość jest udostępniana na .Message Można go ustawić na wersję protokołu SOAP do użycia podczas zapisywania komunikatu lub można ustawić tak, aby None zapobiec wszelkim mapowaniam specyficznym dla protokołu SOAP. Version Jeśli właściwość jest ustawiona na None, metody, które działają z całym komunikatem, działają tak, jakby komunikat składał się tylko z jego treści, na przykład, WriteMessage po prostu wywołać WriteBodyContents zamiast wykonać wiele kroków wymienionych powyżej. Oczekuje się, Version że w przypadku komunikatów przychodzących zostaną automatycznie wykryte i ustawione poprawnie.

Stos kanału

Kanały

Jak wspomniano wcześniej, stos kanału jest odpowiedzialny za konwertowanie wystąpień wychodzących Message na niektóre akcje (takie jak wysyłanie pakietów za pośrednictwem sieci) lub konwertowanie niektórych akcji (takich jak odbieranie pakietów sieciowych) na wystąpienia przychodzące Message .

Stos kanału składa się z co najmniej jednego kanału uporządkowanego w sekwencji. Wystąpienie wychodzące Message jest przekazywane do pierwszego kanału w stosie (nazywanego również najbardziej górnym kanałem), który przekazuje go do następnego kanału w stosie itd. Komunikat kończy się w ostatnim kanale, który jest nazywany kanałem transportu. Przychodzące komunikaty pochodzą z kanału transportu i są przekazywane z kanału do kanału w górę stosu. Z najbardziej górnego kanału komunikat jest zwykle przekazywany do platformy usług. Chociaż jest to zwykły wzorzec komunikatów aplikacji, niektóre kanały mogą działać nieco inaczej, na przykład mogą wysyłać własne komunikaty infrastruktury bez przekazywania komunikatu z kanału powyżej.

Kanały mogą działać na komunikat na różne sposoby, gdy przechodzi przez stos. Najczęstszą operacją jest dodanie nagłówka do wiadomości wychodzącej i odczytywanie nagłówków w wiadomości przychodzącej. Na przykład kanał może obliczyć podpis cyfrowy komunikatu i dodać go jako nagłówek. Kanał może również sprawdzić ten nagłówek podpisu cyfrowego w komunikatach przychodzących i zablokować komunikaty, które nie mają prawidłowego podpisu, z podejmowania drogi w górę stosu kanału. Kanały często ustawiają lub sprawdzają właściwości komunikatów. Treść komunikatu zwykle nie jest modyfikowana, chociaż jest to dozwolone, na przykład kanał zabezpieczeń programu WCF może zaszyfrować treść komunikatu.

Kanały transportu i kodery komunikatów

Najbardziej dolny kanał w stosie jest odpowiedzialny za rzeczywiste przekształcenie wychodzącego Messageelementu , zmodyfikowanego przez inne kanały, w jakąś akcję. Po stronie odbieranej jest to kanał, który konwertuje akcję na Message inny proces kanału.

Jak wspomniano wcześniej, akcje mogą być zróżnicowane: wysyłanie lub odbieranie pakietów sieciowych za pośrednictwem różnych protokołów, odczytywanie lub zapisywanie komunikatu w bazie danych albo kolejkowanie lub kolejkowanie komunikatu w kolejce komunikatów w kolejce kolejkowania komunikatów, aby podać kilka przykładów. Wszystkie te akcje mają jedną wspólną akcję: wymagają przekształcenia między wystąpieniem programu WCFMessage a rzeczywistą grupą bajtów, które można wysyłać, odbierać, odczytywać, zapisywać, zapisywać, kolejkować lub dequeued. Proces konwertowania elementu Message na grupę bajtów jest nazywany kodowaniem, a odwrotny proces tworzenia elementu Message z grupy bajtów jest nazywany dekodowaniem.

Większość kanałów transportu używa składników nazywanych koderami komunikatów , aby wykonać pracę kodowania i dekodowania. Koder komunikatów jest podklasą MessageEncoder klasy . MessageEncoder zawiera różne przeciążenia metod ReadMessage i WriteMessage do konwersji między grupami Message bajtów i .

Po stronie wysyłania kanał transportu buforowania przekazuje Message obiekt otrzymany z kanału powyżej go do WriteMessage. Pobiera on tablicę bajtów, która następnie jest używana do wykonania akcji (na przykład pakowania tych bajtów jako prawidłowych pakietów TCP i wysyłania ich do poprawnego miejsca docelowego). Kanał transportu przesyłania strumieniowego najpierw tworzy Stream element (na przykład za pośrednictwem wychodzącego połączenia TCP), a następnie przekazuje zarówno Stream element , jak i Message musi wysłać do odpowiedniego WriteMessage przeciążenia, które zapisuje komunikat.

Po stronie odbierającej buforowanie kanału transportu wyodrębnia przychodzące bajty (na przykład z przychodzących pakietów TCP) do tablicy i wywołuje ReadMessageMessage obiekt, który może przekazać dalej stos kanału. Kanał transportu przesyłania strumieniowego Stream tworzy obiekt (na przykład strumień sieciowy za pośrednictwem przychodzącego połączenia TCP) i przekazuje go, aby ReadMessage wrócić Message do obiektu.

Separacja między kanałami transportu a koderem komunikatów nie jest obowiązkowa; Istnieje możliwość zapisania kanału transportu, który nie używa kodera komunikatów. Jednak zaletą tej separacji jest łatwość kompozycji. Jeśli kanał transportu używa tylko podstawowego MessageEncoderkanału , może współpracować z dowolnym koderem komunikatów WCF lub innej firmy. Podobnie ten sam koder może być zwykle używany w dowolnym kanale transportu.

Operacja kodera komunikatów

Aby opisać typową operację kodera, warto rozważyć następujące cztery przypadki.

Operacja Komentarz
Kodowanie, buforowane W trybie buforowym koder zwykle tworzy bufor o zmiennym rozmiarze, a następnie tworzy na nim moduł zapisywania XML. Następnie wywołuje WriteMessage(XmlWriter) kodowany komunikat, który zapisuje nagłówki, a następnie treść przy użyciu metody WriteBodyContents(XmlDictionaryWriter), jak wyjaśniono w poprzedniej sekcji Message w tym temacie. Zawartość buforu (reprezentowana jako tablica bajtów) jest następnie zwracana do kanału transportu do użycia.
Kodowanie, przesyłanie strumieniowe W trybie strumieniowym operacja jest podobna do powyższej, ale prostszej. Bufor nie jest potrzebny. Moduł zapisywania XML jest zwykle tworzony za pośrednictwem strumienia i WriteMessage(XmlWriter) jest wywoływany na Message obiekcie , aby zapisać go w tym zapisie.
Dekodowanie, buforowane Podczas dekodowania w trybie buforowym tworzona jest specjalna Message podklasa zawierająca buforowane dane. Nagłówki wiadomości są odczytywane i tworzony jest czytnik XML umieszczony w treści wiadomości. Jest to czytelnik, który zostanie zwrócony za pomocą GetReaderAtBodyContents()polecenia .
Dekodowanie, przesyłanie strumieniowe Podczas dekodowania w trybie strumieniowym tworzona jest zwykle specjalna podklasa Komunikat. Strumień jest wystarczająco zaawansowany, aby odczytać wszystkie nagłówki i umieścić go w treści komunikatu. Czytnik XML jest następnie tworzony za pośrednictwem strumienia. Jest to czytelnik, który zostanie zwrócony za pomocą GetReaderAtBodyContents()polecenia .

Kodery mogą również wykonywać inne funkcje. Na przykład kodery mogą pulować czytniki i zapisy XML. Utworzenie nowego czytnika XML lub modułu zapisywania za każdym razem, gdy jest potrzebny. W związku z tym kodery zwykle utrzymują pulę czytników i pulę składników zapisywania o konfigurowalnym rozmiarze. W opisach opisanej wcześniej operacji kodera, za każdym razem, gdy jest używana fraza "create an XML reader/writer", zwykle oznacza to "wziąć jeden z puli lub utworzyć go, jeśli nie jest dostępny". Koder (i Message podklasy tworzone podczas dekodowania) zawierają logikę zwracania czytników i składników zapisywania do pul, gdy nie są już potrzebne (na przykład po Message zamknięciu).

Program WCF udostępnia trzy kodery komunikatów, chociaż istnieje możliwość utworzenia dodatkowych typów niestandardowych. Podane typy to Mechanizm optymalizacji tekstu, binarnego i transmisji komunikatów (MTOM). Opisano je szczegółowo w temacie Wybieranie kodera komunikatów.

Interfejs IStreamProvider

Podczas pisania komunikatu wychodzącego zawierającego treść strumieniową do modułu zapisywania Message XML metoda używa sekwencji wywołań podobnych do następujących w implementacji OnWriteBodyContents(XmlDictionaryWriter) :

  • Napisz wszelkie niezbędne informacje poprzedzające strumień (na przykład otwierający tag XML).

  • Zapisz strumień.

  • Zapisz wszelkie informacje po strumieniu (na przykład zamykający tag XML).

Działa to dobrze z kodowaniami podobnymi do kodowania tekstowego XML. Jednak niektóre kodowanie nie umieszczają informacji o zestawie informacji XML (na przykład tagów do uruchamiania i kończenia elementów XML) wraz z danymi zawartymi w elementach. Na przykład w kodowaniu MTOM komunikat jest podzielony na wiele części. Jedna część zawiera zestaw informacji XML, który może zawierać odwołania do innych części dla rzeczywistej zawartości elementu. Zestaw informacji XML jest zwykle mały w porównaniu ze strumieniową zawartością, dlatego warto buforować zestaw informacji, zapisywać go, a następnie zapisywać zawartość w strumieniowy sposób. Oznacza to, że po zapisaniu tagu elementu zamykającego strumień nie powinien zostać jeszcze zapisany.

W tym celu IStreamProvider używany jest interfejs. Interfejs ma metodę GetStream() , która zwraca strumień do zapisania. Prawidłowym sposobem na zapisanie treści przesyłanej strumieniowo wiadomości w programie OnWriteBodyContents(XmlDictionaryWriter) jest następująca:

  1. Napisz wszelkie niezbędne informacje poprzedzające strumień (na przykład otwierający tag XML).

  2. Wywołaj WriteValue przeciążenie elementu XmlDictionaryWriter , które przyjmuje element z implementacją IStreamProviderzwracającą IStreamProvider strumień do zapisania.

  3. Zapisz wszelkie informacje po strumieniu (na przykład zamykający tag XML).

W przypadku tego podejścia moduł zapisywania XML może wybrać, kiedy wywoływać GetStream() i zapisywać przesyłane strumieniowo dane. Na przykład tekstowe i binarne zapisy XML będą natychmiast wywoływać i zapisywać strumieniową zawartość między tagami początkowymi i końcowymi. Składnik zapisywania MTOM może zdecydować się na późniejsze wywołanie GetStream() , gdy będzie gotowy do napisania odpowiedniej części wiadomości.

Reprezentowanie danych w programie Service Framework

Zgodnie z opisem w sekcji "Podstawowa architektura" w tym temacie struktura usługi jest częścią programu WCF, która jest między innymi odpowiedzialna za konwertowanie między przyjaznym dla użytkownika modelem programowania na potrzeby danych komunikatów i rzeczywistych Message wystąpień. Zwykle wymiana komunikatów jest reprezentowana w strukturze usług jako metoda programu .NET Framework oznaczona atrybutem OperationContractAttribute . Metoda może przyjmować niektóre parametry i może zwracać wartość zwracaną lub parametry wychodzące (lub oba te parametry). Po stronie usługi parametry wejściowe reprezentują komunikat przychodzący, a wartości zwracane i wyjściowe reprezentują komunikat wychodzący. Po stronie klienta odwrotna wartość to prawda. Model programowania opisujący komunikaty przy użyciu parametrów i zwracaną wartość opisano szczegółowo w temacie Określanie transferu danych w kontraktach usług. Jednak ta sekcja zawiera krótkie omówienie.

Modele programowania

Platforma usług WCF obsługuje pięć różnych modeli programowania do opisywania komunikatów:

1. Pusty komunikat

Jest to najprostszy przypadek. Aby opisać pusty komunikat przychodzący, nie używaj żadnych parametrów wejściowych.

[OperationContract]
int GetCurrentTemperature();
<OperationContract()> Function GetCurrentTemperature() As Integer

Aby opisać pusty komunikat wychodzący, użyj wartości zwracanej void i nie używaj żadnych parametrów wychodzących:

[OperationContract]
void SetDesiredTemperature(int t);
<OperationContract()> Sub SetDesiredTemperature(ByVal t As Integer)

Należy pamiętać, że różni się to od jednokierunkowego kontraktu operacji:

[OperationContract(IsOneWay = true)]
void SetLightbulb(bool isOn);
<OperationContract(IsOneWay:=True)> Sub SetLightbulb(ByVal isOn As Boolean)

W tym przykładzie SetDesiredTemperature opisano wzorzec wymiany komunikatów dwukierunkowych. Komunikat jest zwracany z operacji, ale jest pusty. Istnieje możliwość zwrócenia błędu z operacji. W przykładzie "Ustaw żarówkę" wzorzec wymiany komunikatów jest jednokierunkowy, więc nie ma komunikatu wychodzącego do opisania. Usługa nie może przekazać żadnego stanu z powrotem do klienta w tym przypadku.

2. Bezpośrednie używanie klasy Message

Można użyć Message klasy (lub jednej z jej podklas) bezpośrednio w kontrakcie operacji. W takim przypadku platforma usług przekazuje element Message z operacji do stosu kanału i na odwrót bez dalszego przetwarzania.

Istnieją dwa główne przypadki użycia bezpośredniego użycia Message . Można go użyć w przypadku zaawansowanych scenariuszy, gdy żaden z innych modeli programowania nie zapewnia wystarczającej elastyczności do opisania komunikatu. Na przykład możesz chcieć użyć plików na dysku, aby opisać komunikat, a właściwości pliku stają się nagłówkami komunikatów, a zawartość pliku staje się treścią wiadomości. Następnie możesz utworzyć coś podobnego do poniższego.

public class FileMessage : Message
// Code not shown.
Public Class FileMessage
    Inherits Message
    ' Code not shown.

Drugim typowym zastosowaniem Message w kontrakcie operacji jest to, że usługa nie dba o konkretną zawartość wiadomości i działa na wiadomości jako na czarnej skrzynce. Na przykład możesz mieć usługę, która przekazuje komunikaty do wielu innych adresatów. Umowę można napisać w następujący sposób.

[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

Wiersz Action="*" skutecznie wyłącza wysyłanie komunikatów i zapewnia, że wszystkie komunikaty wysyłane do IForwardingService kontraktu ForwardMessage przejdą do operacji. (Zwykle dyspozytor zbada nagłówek "Akcja" komunikatu, aby określić, do której operacji jest przeznaczona. Action="*" oznacza "wszystkie możliwe wartości nagłówka akcji". Kombinacja action="*" i użycie komunikatu jako parametru jest nazywane "uniwersalnym kontraktem", ponieważ jest w stanie odbierać wszystkie możliwe komunikaty. Aby móc wysyłać wszystkie możliwe komunikaty, użyj komunikatu jako wartości zwracanej i ustaw wartość ReplyAction "*". Uniemożliwi to platformie usług dodanie własnego nagłówka Akcja, co umożliwi kontrolowanie tego nagłówka przy użyciu zwracanego Message obiektu.

3. Kontrakty komunikatów

Program WCF udostępnia deklaratywny model programowania do opisywania komunikatów nazywanych kontraktami komunikatów. Ten model został szczegółowo opisany w temacie Using Message Contracts (Używanie kontraktów komunikatów). Zasadniczo cały komunikat jest reprezentowany przez pojedynczy typ programu .NET Framework, który używa atrybutów, takich jak MessageBodyMemberAttribute i MessageHeaderAttribute , aby opisać, które części klasy kontraktu komunikatów powinny być mapowane na którą część komunikatu.

Kontrakty komunikatów zapewniają dużą kontrolę nad wynikowymi Message wystąpieniami (chociaż oczywiście nie tyle kontroli, jak bezpośrednio przy użyciu Message klasy). Na przykład treści komunikatów często składają się z wielu elementów informacji, z których każdy jest reprezentowany przez własny element XML. Te elementy mogą występować bezpośrednio w treści (tryb nagie ) lub mogą być opakowane w obejmujący element XML. Użycie modelu programowania kontraktów komunikatów umożliwia podjęcie decyzji bez zawijenia i kontrolowania nazwy nazwy otoki i przestrzeni nazw.

Poniższy przykładowy kod kontraktu komunikatów demonstruje te funkcje.

[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

Elementy oznaczone jako serializowane (z atrybutami MessageBodyMemberAttribute, MessageHeaderAttributelub innymi powiązanymi) muszą być serializowane, aby uczestniczyć w umowie komunikatów. Aby uzyskać więcej informacji, zobacz sekcję "Serializacja" w dalszej części tego tematu.

4. Parametry

Często deweloper, który chce opisać operację działającą na wielu fragmentach danych, nie potrzebuje stopnia kontroli, jaką zapewniają kontrakty komunikatów. Na przykład podczas tworzenia nowych usług zwykle nie chce się podejmować decyzji bez zawiniętej i zdecydować o nazwie elementu otoki. Podejmowanie tych decyzji często wymaga głębokiej wiedzy na temat usług sieci Web i protokołu SOAP.

Struktura usług WCF może automatycznie wybierać najlepszą i najbardziej współdziałanie reprezentację protokołu SOAP do wysyłania lub odbierania wielu powiązanych informacji bez wymuszania tych wyborów na użytkownika. Jest to realizowane przez zwykłe opisywanie tych informacji jako parametrów lub zwracanych wartości kontraktu operacji. Rozważmy na przykład następujący kontrakt operacji.

[OperationContract]
void SubmitOrder(string customerID, string item, int quantity);
<OperationContract()> _
Sub SubmitOrder( _
    ByVal customerID As String, _
    ByVal item As String, _
    ByVal quantity As Integer)

Platforma usług automatycznie decyduje się umieścić wszystkie trzy elementy informacji (customerID, , i quantity) w treści komunikatu i opakowować je w element otoki o nazwie SubmitOrderRequestitem.

Opis informacji, które mają być wysyłane lub odbierane jako prosta lista parametrów kontraktu operacji, jest zalecanym podejściem, chyba że istnieją specjalne powody, aby przejść do bardziej złożonego kontraktu komunikatów lub Messagemodeli programowania opartych na modelu.

5. Strumień

Użycie Stream lub jedna z jego podklas w kontrakcie operacji lub jako jedyna część treści komunikatu w kontrakcie komunikatów można uznać za oddzielny model programowania od opisanych powyżej. Użycie Stream w ten sposób jest jedynym sposobem zagwarantowania, że twój kontrakt będzie można używać w strumieniowy sposób, zamiast pisania własnej podklasy zgodnej Message z przesyłaniem strumieniowym. Aby uzyskać więcej informacji, zobacz Duże dane i przesyłanie strumieniowe.

Gdy Stream lub jedna z jego podklas jest używana w ten sposób, serializator nie jest wywoływany. W przypadku komunikatów wychodzących tworzona jest specjalna podklasa przesyłania strumieniowego Message , a strumień jest zapisywany zgodnie z opisem w sekcji w interfejsie IStreamProvider . W przypadku komunikatów przychodzących struktura usługi tworzy podklasę Stream za pośrednictwem komunikatu przychodzącego i udostępnia ją operacji.

Ograniczenia modelu programowania

Opisane powyżej modele programowania nie mogą być dowolnie połączone. Jeśli na przykład operacja akceptuje typ kontraktu komunikatu, kontrakt komunikatu musi być jedynym parametrem wejściowym. Ponadto operacja musi zwrócić pusty komunikat (zwracany typ void) lub inny kontrakt komunikatu. Te ograniczenia modelu programowania opisano w tematach dotyczących każdego konkretnego modelu programowania: Korzystanie z kontraktów komunikatów, używanie klasy komunikatów oraz duże dane i przesyłanie strumieniowe.

Formatery komunikatów

Opisane powyżej modele programowania są obsługiwane przez podłączanie składników nazywanych formaterami komunikatów do platformy usług. Formatery komunikatów to typy, które implementują IClientMessageFormatter interfejs lub IDispatchMessageFormatter lub oba te typy do użycia w klientach i klientach usługi WCF, odpowiednio.

Formatery komunikatów są zwykle podłączane przez zachowania. Na przykład DataContractSerializerOperationBehavior wtyczka w formacie komunikatu kontraktu danych. Odbywa się to po stronie usługi przez ustawienie Formatter poprawnego formatowania w ApplyDispatchBehavior(OperationDescription, DispatchOperation) metodzie lub po stronie klienta przez ustawienie Formatter poprawnego formatowania w metodzie ApplyClientBehavior(OperationDescription, ClientOperation) .

W poniższych tabelach wymieniono metody, które może implementować program formatujący komunikatów.

Interfejs Method Akcja
IDispatchMessageFormatter DeserializeRequest(Message, Object[]) Konwertuje parametry operacji przychodzącej Message na parametry operacji
IDispatchMessageFormatter SerializeReply(MessageVersion, Object[], Object) Tworzy wychodzące Message z wartości zwracanej operacji/parametrów wychodzących
IClientMessageFormatter SerializeRequest(MessageVersion, Object[]) Tworzy wychodzące Message z parametrów operacji
IClientMessageFormatter DeserializeReply(Message, Object[]) Konwertuje przychodzące Message na zwracaną wartość/parametry wychodzące

Serializacja

Za każdym razem, gdy używasz kontraktów komunikatów lub parametrów do opisywania zawartości komunikatów, należy użyć serializacji do konwersji między typami programu .NET Framework i reprezentacją zestawu informacji XML. Serializacja jest używana w innych miejscach w programie WCF, na przykład ma Message metodę Generic GetBody , której można użyć do odczytania całej treści komunikatu deserializowanego w obiekcie.

Program WCF obsługuje dwie technologie serializacji "out of the box" na potrzeby serializacji i deserializacji parametrów i części komunikatówXmlSerializer: i DataContractSerializer . Ponadto można pisać niestandardowe serializatory. Jednak inne części programu WCF (takie jak metoda generic GetBody lub serializacja błędów protokołu SOAP) mogą być ograniczone do używania tylko XmlObjectSerializer podklas (DataContractSerializer i NetDataContractSerializer, ale nie XmlSerializer), lub mogą być nawet zakodowane w celu używania tylko klasy DataContractSerializer.

Jest XmlSerializer to aparat serializacji używany w usługach sieci Web ASP.NET. Jest DataContractSerializer to nowy aparat serializacji, który rozumie nowy model programowania kontraktów danych. DataContractSerializer jest wyborem domyślnym, a wybór użycia XmlSerializer elementu można wykonać dla poszczególnych operacji przy użyciu atrybutu DataContractFormatAttribute .

DataContractSerializerOperationBehavior i XmlSerializerOperationBehavior są zachowaniami operacji odpowiedzialnymi za wtyczkę w formaterach komunikatów odpowiednio dla DataContractSerializer elementów i XmlSerializer. Zachowanie DataContractSerializerOperationBehavior może działać z dowolnym serializatorem pochodzącym z XmlObjectSerializerklasy , w tym elementem NetDataContractSerializer (opisanym szczegółowo w temacie Korzystanie z serializacji autonomicznej). Zachowanie wywołuje jedno z CreateSerializer przeciążeń metody wirtualnej w celu uzyskania serializatora. Aby podłączyć inny serializator, utwórz nową DataContractSerializerOperationBehavior podklasę i przesłoń oba CreateSerializer przeciążenia.

Zobacz też