Używanie klasy Message

Klasa jest podstawowa Message dla programu Windows Communication Foundation (WCF). Cała komunikacja między klientami i usługami ostatecznie skutkuje Message wysyłaniem i odbieraniem wystąpień.

Zwykle nie należy bezpośrednio wchodzić w interakcje z klasą Message . Zamiast tego konstrukcje modelu usług WCF, takie jak kontrakty danych, kontrakty komunikatów i kontrakty operacji, służą do opisywania przychodzących i wychodzących komunikatów. Jednak w niektórych zaawansowanych scenariuszach można programować przy użyciu Message klasy bezpośrednio. Na przykład możesz użyć Message klasy :

  • Jeśli potrzebujesz alternatywnego sposobu tworzenia zawartości komunikatów wychodzących (na przykład tworzenia komunikatu bezpośrednio z pliku na dysku) zamiast serializowania obiektów programu .NET Framework.

  • Jeśli potrzebujesz alternatywnego sposobu korzystania z zawartości komunikatów przychodzących (na przykład gdy chcesz zastosować przekształcenie XSLT do nieprzetworzonej zawartości XML) zamiast deserializacji do obiektów programu .NET Framework.

  • Gdy musisz radzić sobie z komunikatami w ogólny sposób niezależnie od zawartości komunikatu (na przykład podczas kierowania lub przesyłania komunikatów podczas tworzenia routera, modułu równoważenia obciążenia lub systemu publikowania-subskrybowania).

Przed rozpoczęciem Message korzystania z klasy zapoznaj się z architekturą transferu danych WCF w temacie Omówienie architektury transferu danych.

A Message jest kontenerem ogólnego przeznaczenia dla danych, ale jego projekt ściśle jest zgodny z projektem komunikatu w protokole SOAP. Podobnie jak w przypadku protokołu SOAP komunikat ma zarówno treść komunikatu, jak i nagłówki. Treść komunikatu zawiera rzeczywiste dane ładunku, a nagłówki zawierają dodatkowe nazwane kontenery danych. Reguły odczytywania i zapisywania treści i nagłówków są różne, na przykład nagłówki są zawsze buforowane w pamięci i mogą być dostępne w dowolnej kolejności w dowolnej kolejności, podczas gdy treść może być odczytywana tylko raz i może być przesyłana strumieniowo. Zwykle w przypadku korzystania z protokołu SOAP treść komunikatu jest mapowana na treść protokołu SOAP, a nagłówki komunikatów są mapowane na nagłówki protokołu SOAP.

Używanie klasy message w operacjach

Możesz użyć Message klasy jako parametru wejściowego operacji, wartości zwracanej operacji lub obu tych operacji. Jeśli Message jest używany w dowolnym miejscu operacji, obowiązują następujące ograniczenia:

  • Operacja nie może mieć żadnych out parametrów ani ref .

  • Nie można mieć więcej niż jednego input parametru. Jeśli parametr jest obecny, musi to być komunikat lub typ kontraktu komunikatu.

  • Zwracany typ musi być voidtypu , Messagelub typu kontraktu komunikatu.

Poniższy przykład kodu zawiera prawidłowy kontrakt operacji.

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    Message GetData();

    [OperationContract]
    void PutData(Message m);
}
<ServiceContract()> _
Public Interface IMyService
    <OperationContract()> _
    Function GetData() As Message

    <OperationContract()> _
    Sub PutData(ByVal m As Message)
End Interface

Tworzenie podstawowych komunikatów

Klasa Message udostępnia metody fabryki statycznej CreateMessage , których można użyć do tworzenia podstawowych komunikatów.

Wszystkie CreateMessage przeciążenia przyjmują parametr wersji typu MessageVersion , który wskazuje wersje protokołu SOAP i WS-Addressing do użycia dla komunikatu. Jeśli chcesz użyć tych samych wersji protokołu co komunikat przychodzący, możesz użyć IncomingMessageVersion właściwości w OperationContext wystąpieniu uzyskanym Current z właściwości . Większość CreateMessage przeciążeń ma również parametr ciągu, który wskazuje akcję PROTOKOŁU SOAP do użycia dla komunikatu. Wersję można ustawić tak, aby None wyłączyć generowanie kopert protokołu SOAP. Komunikat składa się tylko z treści.

Tworzenie komunikatów z obiektów

Najbardziej podstawowe CreateMessage przeciążenie, które przyjmuje tylko wersję, a akcja tworzy komunikat z pustą treścią. Kolejne przeciążenie przyjmuje dodatkowy Object parametr. Spowoduje to utworzenie komunikatu, którego treść jest serializowaną reprezentacją danego obiektu. Użyj opcji DataContractSerializer z ustawieniami domyślnymi na potrzeby serializacji. Jeśli chcesz użyć innego serializatora lub chcesz DataContractSerializer skonfigurować inaczej, użyj CreateMessage przeciążenia, które przyjmuje XmlObjectSerializer również parametr.

Aby na przykład zwrócić obiekt w komunikacie, możesz użyć następującego kodu.

public class MyService1 : IMyService
{
    public Message GetData()
    {
        Person p = new Person();
        p.name = "John Doe";
        p.age = 42;
        MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
        return Message.CreateMessage(ver, "GetDataResponse", p);
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
[DataContract]
public class Person
{
    [DataMember] public string name;
    [DataMember] public int age;
}
Public Class MyService1
    Implements IMyService

    Public Function GetData() As Message _
     Implements IMyService.GetData
        Dim p As New Person()
        p.name = "John Doe"
        p.age = 42
        Dim ver As MessageVersion = _
          OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, "GetDataResponse", p)

    End Function


    Public Sub PutData(ByVal m As Message) _
    Implements IMyService.PutData
        ' Not implemented.
    End Sub
End Class
<DataContract()> _
Public Class Person
    <DataMember()> _
    Public name As String
    <DataMember()> _
    Public age As Integer
End Class

Tworzenie komunikatów z czytników XML

CreateMessage Istnieją przeciążenia, które przyjmują element XmlReader lub XmlDictionaryReader dla treści zamiast obiektu. W takim przypadku treść komunikatu zawiera kod XML, który wynika z odczytu z przekazanego czytnika XML. Na przykład poniższy kod zwraca komunikat z zawartością treści odczytywaną z pliku XML.

public class MyService2 : IMyService
{
    public Message GetData()
    {
        FileStream stream = new FileStream("myfile.xml",FileMode.Open);
        XmlDictionaryReader xdr =
               XmlDictionaryReader.CreateTextReader(stream,
                           new XmlDictionaryReaderQuotas());
        MessageVersion ver =
            OperationContext.Current.IncomingMessageVersion;
        return Message.CreateMessage(ver,"GetDataResponse",xdr);
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
Public Class MyService2
    Implements IMyService

    Public Function GetData() As Message Implements IMyService.GetData
        Dim stream As New FileStream("myfile.xml", FileMode.Open)
        Dim xdr As XmlDictionaryReader = _
        XmlDictionaryReader.CreateTextReader(stream, New XmlDictionaryReaderQuotas())
        Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, "GetDataResponse", xdr)

    End Function


    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData

    End Sub
End Class

Ponadto istnieją CreateMessage przeciążenia, które przyjmują element XmlReader lub XmlDictionaryReader , który reprezentuje cały komunikat, a nie tylko treść. Te przeciążenia przyjmują również parametr liczby całkowitej maxSizeOfHeaders . Nagłówki są zawsze buforowane w pamięci zaraz po utworzeniu komunikatu, a ten parametr ogranicza ilość buforowania, która ma miejsce. Należy ustawić ten parametr na bezpieczną wartość, jeśli kod XML pochodzi z niezaufanego źródła w celu ograniczenia możliwości ataku typu "odmowa usługi". Wersje protokołu SOAP i adresowania WS komunikatu, które reprezentuje czytnik XML, muszą być zgodne z wersjami wskazanymi przy użyciu parametru wersji.

Tworzenie komunikatów za pomocą narzędzia BodyWriter

Jedno CreateMessage przeciążenie przyjmuje BodyWriter wystąpienie, aby opisać treść komunikatu. A BodyWriter to abstrakcyjna klasa, która może być pochodna w celu dostosowania sposobu tworzenia treści komunikatów. Możesz utworzyć własną BodyWriter klasę pochodną, aby opisać treść komunikatów w niestandardowy sposób. Należy zastąpić BodyWriter.OnWriteBodyContents metodę , która przyjmuje metodę XmlDictionaryWriter; ta metoda jest odpowiedzialna za zapisywanie treści.

Zapisy treści mogą być buforowane lub niebuforowane (przesyłane strumieniowo). Buforowane autorzy treści mogą zapisywać ich zawartość dowolną liczbę razy, podczas gdy przesyłane strumieniowo mogą zapisywać ich zawartość tylko raz. Właściwość IsBuffered wskazuje, czy składnik zapisywania treści jest buforowany, czy nie. Można to ustawić dla składnika zapisywania treści, wywołując chroniony BodyWriter konstruktor, który przyjmuje isBuffered parametr logiczny. Autorzy treści wspierają tworzenie buforowanego składnika zapisywania ciała z niebuforowanego składnika zapisywania ciała. Możesz zastąpić metodę OnCreateBufferedCopy , aby dostosować ten proces. Domyślnie używany jest bufor w pamięci, który zawiera kod XML zwracany przez OnWriteBodyContents program . OnCreateBufferedCopy przyjmuje parametr liczby całkowitej maxBufferSize . Jeśli zastąpisz tę metodę, nie można utworzyć buforów większych niż ten maksymalny rozmiar.

Klasa BodyWriter udostępnia WriteBodyContents metody i CreateBufferedCopy , które są zasadniczo cienkimi otokami wokół OnWriteBodyContents i OnCreateBufferedCopy metodami. Te metody przeprowadzają sprawdzanie stanu, aby upewnić się, że moduł zapisywania treści bez buforowania nie jest uzyskiwany więcej niż raz. Te metody są wywoływane bezpośrednio tylko podczas tworzenia niestandardowych klas pochodnych Message na BodyWriterspodstawie klasy .

Tworzenie komunikatów o błędach

Aby utworzyć komunikaty o błędach protokołu SOAP, można użyć pewnych CreateMessage przeciążeń. Najbardziej podstawowe z nich przyjmuje MessageFault obiekt opisujący błąd. Inne przeciążenia są zapewniane dla wygody. Pierwsze takie przeciążenie przyjmuje FaultCode ciąg i i tworzy MessageFault element przy użyciu MessageFault.CreateFault tych informacji. Inne przeciążenie przyjmuje obiekt szczegółów, a także przekazuje go do CreateFault razem z kodem błędu i przyczyną. Na przykład następująca operacja zwraca błąd.

public class MyService3 : IMyService
{
    public Message GetData()
    {
        FaultCode fc = new FaultCode("Receiver");
        MessageVersion ver = OperationContext.Current.IncomingMessageVersion;
            return Message.CreateMessage(ver,fc,"Bad data","GetDataResponse");
    }

    public void PutData(Message m)
    {
        // Not implemented.
    }
}
Public Class MyService3
    Implements IMyService

    Public Function GetData() As Message Implements IMyService.GetData
        Dim fc As New FaultCode("Receiver")
        Dim ver As MessageVersion = OperationContext.Current.IncomingMessageVersion
        Return Message.CreateMessage(ver, fc, "Bad data", "GetDataResponse")

    End Function


    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData

    End Sub
End Class

Wyodrębnianie danych treści komunikatu

Klasa Message obsługuje wiele sposobów wyodrębniania informacji z jego treści. Można je sklasyfikować w następujących kategoriach:

  • Pobieranie całej treści komunikatu zapisanej jednocześnie w składniku zapisywania XML. Jest to nazywane pisaniem wiadomości.

  • Pobieranie czytnika XML przez treść komunikatu. Dzięki temu można później uzyskać dostęp do treści komunikatu zgodnie z wymaganiami. Jest to nazywane odczytywaniem komunikatu.

  • Cały komunikat, w tym jego treść, można skopiować do buforu MessageBuffer w pamięci typu. Jest to nazywane kopiowaniem komunikatu.

Dostęp do treści obiektu Message można uzyskać tylko raz, niezależnie od sposobu uzyskiwania do niego dostępu. Obiekt komunikatu State ma właściwość, która jest początkowo ustawiona na Wartość Utworzono. Trzy metody dostępu opisane na powyższej liście ustawiają odpowiednio stan Na zapisane, Odczyt i Skopiowane. Ponadto metoda może ustawić stan Na Zamknięty, Close gdy zawartość treści komunikatu nie jest już wymagana. Dostęp do treści wiadomości można uzyskać tylko w stanie Utworzono i nie ma możliwości powrotu do stanu Utworzone po zmianie stanu.

Pisanie komunikatów

Metoda WriteBodyContents(XmlDictionaryWriter) zapisuje zawartość treści danego Message wystąpienia dla danego modułu zapisywania XML. Metoda WriteBody wykonuje to samo, z tą różnicą, że otacza zawartość treści w odpowiednim elemplecie otoki (na przykładsoap:body<> ). WriteMessage Na koniec zapisuje całą wiadomość, w tym zawijaną kopertę PROTOKOŁU SOAP i nagłówki. Jeśli protokół SOAP jest wyłączony (Version to MessageVersion.None), wszystkie trzy metody wykonują te same czynności: zapisują zawartość treści wiadomości.

Na przykład poniższy kod zapisuje treść komunikatu przychodzącego do pliku.

public class MyService4 : IMyService
{
    public void PutData(Message m)
    {
        FileStream stream = new FileStream("myfile.xml",FileMode.Create);
        XmlDictionaryWriter xdw =
            XmlDictionaryWriter.CreateTextWriter(stream);
        m.WriteBodyContents(xdw);
        xdw.Flush();
    }

    public Message GetData()
    {
        throw new NotImplementedException();
    }
}
Public Class MyService4
    Implements IMyService

    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
        Dim stream As New FileStream("myfile.xml", FileMode.Create)
        Dim xdw As XmlDictionaryWriter = XmlDictionaryWriter.CreateTextWriter(stream)
        m.WriteBodyContents(xdw)
        xdw.Flush()

    End Sub


    Public Function GetData() As Message Implements IMyService.GetData
        Throw New NotImplementedException()

    End Function
End Class

Dwie dodatkowe metody pomocnicze zapisują pewne tagi elementów startowych protokołu SOAP. Te metody nie uzyskują dostępu do treści komunikatu, dlatego nie zmieniają stanu komunikatu. Są to:

  • WriteStartBody zapisuje element treści początkowej, na przykład <soap:Body>.

  • WriteStartEnvelope zapisuje element koperty początkowej, na przykład <soap:Envelope>.

Aby napisać odpowiednie tagi elementów końcowych, wywołaj WriteEndElement odpowiedni moduł zapisywania XML. Te metody są rzadko wywoływane bezpośrednio.

Odczytywanie komunikatów

Podstawowym sposobem odczytania treści komunikatu jest wywołanie metody GetReaderAtBodyContents. Otrzymasz komunikat XmlDictionaryReader , którego możesz użyć do odczytania treści wiadomości. Należy pamiętać, że Message przejścia do stanu Odczyt natychmiast GetReaderAtBodyContents po wywołaniu, a nie w przypadku używania zwróconego czytnika XML.

Metoda GetBody umożliwia również dostęp do treści komunikatu jako obiektu wpisanego. Wewnętrznie ta metoda używa GetReaderAtBodyContentsmetody , a więc również przenosi stan komunikatu Read do stanu (zobacz State właściwość).

Dobrym rozwiązaniem jest sprawdzenie właściwości, w tym przypadku treść komunikatu IsEmpty jest pusta i GetReaderAtBodyContents zgłasza błąd InvalidOperationException. Ponadto, jeśli jest to odebrana wiadomość (na przykład odpowiedź), możesz również sprawdzić IsFault, co wskazuje, czy komunikat zawiera błąd.

Najbardziej podstawowe przeciążenie deserializuje treść komunikatu GetBody do wystąpienia typu (wskazanego przez parametr ogólny) przy użyciu DataContractSerializer skonfigurowanego z ustawieniami domyślnymi i z wyłączonym limitem przydziału MaxItemsInObjectGraph . Jeśli chcesz użyć innego aparatu serializacji lub skonfigurować DataContractSerializer w sposób inny niż domyślny, użyj GetBody przeciążenia, które przyjmuje XmlObjectSerializer.

Na przykład poniższy kod wyodrębnia dane z treści komunikatu zawierającego serializowany Person obiekt i wyświetla imię i nazwisko osoby.

    public class MyService5 : IMyService
    {
        public void PutData(Message m)
        {
            Person p = m.GetBody<Person>();
            Console.WriteLine(p.name);
        }

        public Message GetData()
        {
            throw new NotImplementedException();
        }
    }
}
namespace Samples2
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        Message GetData();

        [OperationContract]
        void PutData(Message m);
    }

    [DataContract]
    public class Person
    {
        [DataMember] public string name;
        [DataMember] public int age;
    }
    Public Class MyService5
        Implements IMyService

        Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
            Dim p As Person = m.GetBody(Of Person)()
            Console.WriteLine(p.name)

        End Sub


        Public Function GetData() As Message Implements IMyService.GetData
            Throw New NotImplementedException()

        End Function
    End Class
End Namespace
Namespace Samples2
    <ServiceContract()> _
    Public Interface IMyService
        <OperationContract()> _
        Function GetData() As Message

        <OperationContract()> _
        Sub PutData(ByVal m As Message)
    End Interface

    <DataContract()> _
    Public Class Person
        <DataMember()> _
        Public name As String
        <DataMember()> _
        Public age As Integer
    End Class

Kopiowanie komunikatu do buforu

Czasami konieczne jest uzyskanie dostępu do treści komunikatu więcej niż raz, na przykład, aby przekazać tę samą wiadomość do wielu miejsc docelowych w ramach systemu wydawcy-subskrybenta. W takim przypadku należy buforować cały komunikat (w tym treść) w pamięci. Możesz to zrobić, wywołując polecenie CreateBufferedCopy(Int32). Ta metoda przyjmuje parametr liczby całkowitej, który reprezentuje maksymalny rozmiar buforu i tworzy bufor nie większy niż ten rozmiar. Należy ustawić tę wartość na bezpieczną wartość, jeśli komunikat pochodzi z niezaufanego źródła.

Bufor jest zwracany jako MessageBuffer wystąpienie. Dostęp do danych w buforze można uzyskać na kilka sposobów. Podstawowym sposobem jest wywołanie CreateMessage metody w celu utworzenia Message wystąpień z buforu.

Innym sposobem uzyskiwania dostępu do danych w buforze jest zaimplementowanie interfejsu IXPathNavigable implementowania MessageBuffer klasy w celu uzyskania bezpośredniego dostępu do bazowego kodu XML. Niektóre CreateNavigator przeciążenia umożliwiają tworzenie System.Xml.XPath nawigatorów chronionych przez limit przydziału węzłów, ograniczając liczbę węzłów XML, które można odwiedzić. Pomaga to zapobiec atakom typu "odmowa usługi" na podstawie długiego czasu przetwarzania. Ten cudzysłów jest domyślnie wyłączony. Niektóre CreateNavigator przeciążenia umożliwiają określenie sposobu obsługi odstępu w kodzie XML przy użyciu XmlSpace wyliczenia , a wartość domyślna to XmlSpace.None.

Ostatnim sposobem uzyskania dostępu do zawartości buforu komunikatów jest zapisanie jego zawartości w strumieniu przy użyciu polecenia WriteMessage.

W poniższym przykładzie pokazano proces pracy z MessageBuffer: wiadomość przychodząca jest przesyłana do wielu adresatów, a następnie rejestrowana w pliku. Bez buforowania nie jest to możliwe, ponieważ treść komunikatu może być dostępna tylko raz.

[ServiceContract]
public class ForwardingService
{
    private List<IOutputChannel> forwardingAddresses;

    [OperationContract]
    public void ForwardMessage (Message m)
    {
        //Copy the message to a buffer.
        MessageBuffer mb = m.CreateBufferedCopy(65536);

        //Forward to multiple recipients.
        foreach (IOutputChannel channel in forwardingAddresses)
        {
            Message copy = mb.CreateMessage();
            channel.Send(copy);
        }

        //Log to a file.
        FileStream stream = new FileStream("log.xml",FileMode.Append);
        mb.WriteMessage(stream);
        stream.Flush();
    }
}
<ServiceContract()> _
Public Class ForwardingService
    Private forwardingAddresses As List(Of IOutputChannel)

    <OperationContract()> _
    Public Sub ForwardMessage(ByVal m As Message)
        'Copy the message to a buffer.
        Dim mb As MessageBuffer = m.CreateBufferedCopy(65536)

        'Forward to multiple recipients.
        Dim channel As IOutputChannel
        For Each channel In forwardingAddresses
            Dim copy As Message = mb.CreateMessage()
            channel.Send(copy)
        Next channel

        'Log to a file.
        Dim stream As New FileStream("log.xml", FileMode.Append)
        mb.WriteMessage(stream)
        stream.Flush()

    End Sub
End Class

Klasa MessageBuffer ma innych członków, których warto zauważyć. Close Metodę można wywołać, aby zwolnić zasoby, gdy zawartość buforu nie jest już wymagana. Właściwość BufferSize zwraca rozmiar przydzielonego buforu. Właściwość MessageContentType zwraca typ zawartości MIME komunikatu.

Uzyskiwanie dostępu do treści komunikatu na potrzeby debugowania

W celach debugowania można wywołać ToString metodę , aby uzyskać reprezentację komunikatu jako ciąg. Ta reprezentacja zazwyczaj pasuje do sposobu, w jaki komunikat będzie wyglądał na przewodzie, gdyby został zakodowany przy użyciu kodera tekstowego, z tą różnicą, że kod XML byłby lepiej sformatowany pod kątem czytelności ludzkiej. Jednym z wyjątków jest treść komunikatu. Treść może być odczytywana tylko raz i ToString nie zmienia stanu komunikatu. W związku z tym ToString metoda może nie być w stanie uzyskać dostępu do treści i może zastąpić symbol zastępczy (na przykład "..." lub trzy kropki) zamiast treści wiadomości. W związku z tym nie należy używać ToString do rejestrowania komunikatów, jeśli zawartość treści komunikatów jest ważna.

Uzyskiwanie dostępu do innych części komunikatów

Różne właściwości są udostępniane w celu uzyskania dostępu do informacji o komunikacie innym niż jego zawartość treści. Nie można ich jednak wywołać po zamknięciu komunikatu:

  • Właściwość Headers reprezentuje nagłówki komunikatów. Zobacz sekcję dotyczącą pracy z nagłówkami w dalszej części tego tematu.

  • Właściwość Properties reprezentuje właściwości komunikatu, które są elementami nazwanych danych dołączonych do komunikatu, które zazwyczaj nie są emitowane podczas wysyłania komunikatu. Zobacz sekcję "Praca z właściwościami" w dalszej części tego tematu.

  • Właściwość Version wskazuje wersję protokołu SOAP i adresowania WS skojarzoną z komunikatem lub None jeśli protokół SOAP jest wyłączony.

  • Właściwość IsFault zwraca true wartość , jeśli komunikat jest komunikatem o błędzie protokołu SOAP.

  • Właściwość IsEmpty zwraca true wartość , jeśli komunikat jest pusty.

Za pomocą GetBodyAttribute(String, String) metody można uzyskać dostęp do określonego atrybutu w elemecie otoki treści (na przykład <soap:Body>) identyfikowanym przez określoną nazwę i przestrzeń nazw. Jeśli taki atrybut nie zostanie znaleziony, null zostanie zwrócony. Tę metodę można wywołać tylko wtedy, gdy Message element znajduje się w stanie Utworzono (gdy treść komunikatu nie została jeszcze dostępna).

Praca z nagłówkami

Element Message może zawierać dowolną liczbę nazwanych fragmentów XML, nazywanych nagłówkami. Każdy fragment jest zwykle mapowy na nagłówek PROTOKOŁU SOAP. Dostęp do nagłówków jest uzyskiwany za pośrednictwem Headers właściwości typu MessageHeaders. MessageHeaders jest kolekcją MessageHeaderInfo obiektów, a do poszczególnych nagłówków można uzyskać dostęp za pośrednictwem interfejsu IEnumerable lub za pośrednictwem indeksatora. Na przykład poniższy kod wyświetla nazwy wszystkich nagłówków w obiekcie Message.

public class MyService6 : IMyService
{
    public void PutData(Message m)
    {
        foreach (MessageHeaderInfo mhi in m.Headers)
        {
            Console.WriteLine(mhi.Name);
        }
    }

    public Message GetData()
    {
        throw new NotImplementedException();
    }
}
Public Class MyService6
    Implements IMyService

    Public Sub PutData(ByVal m As Message) Implements IMyService.PutData
        Dim mhi As MessageHeaderInfo
        For Each mhi In m.Headers
            Console.WriteLine(mhi.Name)
        Next mhi

    End Sub


    Public Function GetData() As Message Implements IMyService.GetData
        Throw New NotImplementedException()

    End Function
End Class

Dodawanie, usuwanie, znajdowanie nagłówków

Możesz dodać nowy nagłówek na końcu wszystkich istniejących nagłówków przy użyciu Add metody . Możesz użyć Insert metody , aby wstawić nagłówek w określonym indeksie. Istniejące nagłówki są przesuwane dla wstawionego elementu. Nagłówki są uporządkowane według ich indeksu, a pierwszy dostępny indeks to 0. Możesz użyć różnych CopyHeadersFrom przeciążeń metody, aby dodać nagłówki z innego Message wystąpienia lub MessageHeaders . Niektóre przeciążenia kopiują jeden pojedynczy nagłówek, a inne kopiują wszystkie z nich. Metoda Clear usuwa wszystkie nagłówki. Metoda RemoveAt usuwa nagłówek w określonym indeksie (przesuwając wszystkie nagłówki po nim). Metoda RemoveAll usuwa wszystkie nagłówki z określoną nazwą i przestrzenią nazw.

Pobierz określony nagłówek przy użyciu FindHeader metody . Ta metoda przyjmuje nazwę i przestrzeń nazw nagłówka do znalezienia i zwraca jej indeks. Jeśli nagłówek występuje więcej niż raz, zgłaszany jest wyjątek. Jeśli nagłówek nie zostanie znaleziony, zwraca wartość -1.

W modelu nagłówka protokołu SOAP nagłówki mogą mieć wartość określającą Actor zamierzonego adresata nagłówka. Najbardziej podstawowe FindHeader przeciążenie wyszukuje tylko nagłówki przeznaczone dla ostatecznego odbiorcy komunikatu. Jednak inne przeciążenie umożliwia określenie, które Actor wartości są uwzględnione w wyszukiwaniu. Aby uzyskać więcej informacji, zobacz specyfikację protokołu SOAP.

Metoda CopyTo(MessageHeaderInfo[], Int32) jest udostępniana do kopiowania nagłówków z MessageHeaders kolekcji do tablicy MessageHeaderInfo obiektów.

Aby uzyskać dostęp do danych XML w nagłówku, możesz wywołać GetReaderAtHeader i zwrócić czytnik XML dla określonego indeksu nagłówka. Jeśli chcesz deserializować zawartość nagłówka do obiektu, użyj GetHeader<T>(Int32) lub jednego z innych przeciążeń. Najbardziej podstawowe przeciążenia deserializacji nagłówków przy użyciu skonfigurowanego DataContractSerializer w domyślny sposób. Jeśli chcesz użyć innego serializatora lub innej konfiguracji DataContractSerializerprogramu , użyj jednego z przeciążeń, które przyjmują element XmlObjectSerializer. Istnieją również przeciążenia, które przyjmują nazwę nagłówka, przestrzeń nazw i opcjonalnie listę Actor wartości zamiast indeksu; jest to kombinacja FindHeader elementów i GetHeader.

Praca z właściwościami

Wystąpienie Message może zawierać dowolną liczbę nazwanych obiektów dowolnego typu. Ta kolekcja jest dostępna za pośrednictwem Properties właściwości typu MessageProperties. Kolekcja implementuje IDictionary<TKey,TValue> interfejs i działa jako mapowanie z String do Object. Zwykle wartości właściwości nie są mapowanie bezpośrednio na żadną część komunikatu w sieci, ale raczej zapewniają różne wskazówki dotyczące przetwarzania komunikatów do różnych kanałów w stosie kanału WCF lub w CopyTo(MessageHeaderInfo[], Int32) strukturze usług. Aby zapoznać się z przykładem, zobacz Omówienie architektury transferu danych.

Dziedziczenie z klasy komunikatów

Jeśli wbudowane typy komunikatów utworzone przy użyciu polecenia CreateMessage nie spełniają wymagań, utwórz klasę pochodzącą Message z klasy .

Definiowanie zawartości treści wiadomości

Istnieją trzy podstawowe techniki uzyskiwania dostępu do danych w treści komunikatu: zapisywanie, odczytywanie i kopiowanie ich do buforu. Te operacje ostatecznie powodują OnWriteBodyContentswywoływanie metod , OnGetReaderAtBodyContentsi OnCreateBufferedCopy odpowiednio w klasie pochodnej Messageklasy . Klasa bazowa Message gwarantuje, że tylko jedna z tych metod jest wywoływana dla każdego Message wystąpienia i że nie jest wywoływana więcej niż raz. Klasa bazowa zapewnia również, że metody nie są wywoływane w zamkniętym komunikacie. Nie ma potrzeby śledzenia stanu komunikatu w implementacji.

OnWriteBodyContents jest metodą abstrakcyjną i musi być zaimplementowana. Najprostszym sposobem zdefiniowania treści wiadomości jest napisanie przy użyciu tej metody. Na przykład następujący komunikat zawiera 100 000 losowych liczb z zakresu od 1 do 20.

public class RandomMessage : Message
{
    override protected  void  OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        Random r = new Random();
        for (int i = 0; i <100000; i++)
        {
            writer.WriteStartElement("number");
            writer.WriteValue(r.Next(1,20));
            writer.WriteEndElement();
        }
    }
    //code omitted…
Public Class RandomMessage
    Inherits Message

    Protected Overrides Sub OnWriteBodyContents( _
            ByVal writer As XmlDictionaryWriter)
        Dim r As New Random()
        Dim i As Integer
        For i = 0 To 99999
            writer.WriteStartElement("number")
            writer.WriteValue(r.Next(1, 20))
            writer.WriteEndElement()
        Next i

    End Sub
    ' Code omitted.

Metody OnGetReaderAtBodyContents() i OnCreateBufferedCopy mają domyślne implementacje, które działają w większości przypadków. Domyślne implementacje wywołają OnWriteBodyContentsmetodę , buforują wyniki i współpracują z wynikowym buforem. Jednak w niektórych przypadkach może to nie wystarczyć. W poprzednim przykładzie odczytywanie komunikatu powoduje buforowanie 100 000 elementów XML, co może nie być pożądane. Możesz przesłonić OnGetReaderAtBodyContents() , aby zwrócić niestandardową XmlDictionaryReader klasę pochodną, która obsługuje liczby losowe. Następnie można przesłonić OnWriteBodyContents , aby użyć czytnika zwracanego przez OnGetReaderAtBodyContents() metodę, jak pokazano w poniższym przykładzie.

    public override MessageHeaders Headers
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageProperties Properties
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageVersion Version
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }
}

public class RandomMessage2 : Message
{
    override protected XmlDictionaryReader OnGetReaderAtBodyContents()
    {
    return new RandomNumbersXmlReader();
    }

    override protected void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        XmlDictionaryReader xdr = OnGetReaderAtBodyContents();
        writer.WriteNode(xdr, true);
    }
    public override MessageHeaders Headers
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageProperties Properties
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }

    public override MessageVersion Version
    {
        get { throw new Exception("The method or operation is not implemented."); }
    }
}

public class RandomNumbersXmlReader : XmlDictionaryReader
{
    //code to serve up 100000 random numbers in XML form omitted…

    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 Properties() As MessageProperties
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Version() As MessageVersion
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property
End Class

Public Class RandomMessage2
    Inherits Message

    Protected Overrides Function OnGetReaderAtBodyContents() As XmlDictionaryReader
        Return New RandomNumbersXmlReader()

    End Function


    Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter)
        Dim xdr As XmlDictionaryReader = OnGetReaderAtBodyContents()
        writer.WriteNode(xdr, True)

    End Sub

    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 Properties() As MessageProperties
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property

    Public Overrides ReadOnly Property Version() As MessageVersion
        Get
            Throw New Exception("The method or operation is not implemented.")
        End Get
    End Property
End Class

Public Class RandomNumbersXmlReader
    Inherits XmlDictionaryReader
    'code to serve up 100000 random numbers in XML form omitted

Podobnie możesz przesłonić OnCreateBufferedCopy , aby zwrócić własną MessageBuffer klasę pochodną.

Oprócz dostarczania zawartości treści komunikatu klasa pochodna wiadomości musi również zastąpić Versionwłaściwości , Headersi Properties .

Pamiętaj, że jeśli tworzysz kopię wiadomości, kopia używa nagłówków wiadomości z oryginalnego elementu .

Inne elementy członkowskie, które mogą być zastępowane

Można zastąpić OnWriteStartEnvelopemetody , i OnWriteStartBody , OnWriteStartHeadersaby określić sposób zapisywania tagów początkowych elementu treści protokołu SOAP, nagłówków soap i elementu treści protokołu SOAP. Zwykle odpowiadają <soap:Envelope>one , <soap:Header>i <soap:Body>. Te metody zwykle nie powinny zapisywać żadnych elementów, jeśli Version właściwość zwraca Nonewartość .

Uwaga

Domyślna implementacja wywołań OnGetReaderAtBodyContentsOnWriteStartEnvelope i OnWriteStartBody przed wywołaniem OnWriteBodyContents i buforowaniem wyników. Nagłówki nie są zapisywane.

Zastąpij metodę OnWriteMessage , aby zmienić sposób konstruowania całego komunikatu z różnych elementów. Metoda jest wywoływana OnWriteMessage z WriteMessage i z domyślnej OnCreateBufferedCopy implementacji. Należy pamiętać, że zastępowanie WriteMessage nie jest najlepszym rozwiązaniem. Lepiej zastąpić odpowiednie On metody (na przykład OnWriteStartEnvelope, , OnWriteStartHeadersi OnWriteBodyContents.

Zastąpij OnBodyToString , aby zastąpić sposób reprezentowania treści komunikatu podczas debugowania. Wartością domyślną jest reprezentacja jej jako trzy kropki ("..."). Należy pamiętać, że ta metoda może być wywoływana wiele razy, gdy stan komunikatu jest inny niż Zamknięty. Implementacja tej metody nigdy nie powinna powodować żadnej akcji, która musi być wykonywana tylko raz (na przykład odczytywanie z strumienia tylko do przodu).

Zastąpij metodę OnGetBodyAttribute , aby zezwolić na dostęp do atrybutów w elemecie treści protokołu SOAP. Tę metodę można wywołać dowolną liczbę razy, ale Message typ podstawowy gwarantuje, że jest wywoływany tylko wtedy, gdy komunikat znajduje się w stanie Utworzono. Nie jest wymagane sprawdzenie stanu w implementacji. Domyślna implementacja zawsze zwraca wartość null, co oznacza, że w elemecie treści nie ma żadnych atrybutów.

Message Jeśli obiekt musi wykonać jakiekolwiek specjalne czyszczenie, gdy treść komunikatu nie jest już wymagana, możesz zastąpić OnCloseelement . Domyślna implementacja nic nie robi.

Właściwości IsEmpty i IsFault można zastąpić. Domyślnie obie zwracają wartość false.