Verwenden der Message-Klasse
Die Message-Klasse ist für Windows Communication Foundation (WCF) von grundlegender Bedeutung. Die gesamte Kommunikation zwischen Clients und Diensten führt letztlich zu gesendeten und empfangenen Message-Instanzen.
Sie würden normalerweise nicht direkt mit der Message-Klasse interagieren. Stattdessen werden WCF-Dienstmodellkonstrukte wie Daten-, Nachrichten- und Vorgangsverträge zum Beschreiben von ein- und ausgehenden Nachrichten verwendet. In einigen komplexen Szenarien können Sie jedoch direkt mit der Message-Klasse programmieren. Die Verwendung der Message-Klasse kann beispielsweise in den folgenden Fällen erforderlich sein:
Wenn Sie eine alternative Methode zum Erstellen des Inhalts ausgehender Nachrichten (z. B. einer Nachricht direkt aus einer Datei auf der Festplatte) statt des Serialisierens von .NET Framework-Objekten benötigen.
Wenn Sie eine alternative Methode zum Verwenden des Inhalts eingehender Nachrichten (z. B. zum Anwenden einer XSLT-Transformation auf unformatierte XML-Inhalte) statt des Deserialisierens in .NET Framework-Objekte benötigen.
Wenn Sie Nachrichten allgemein und unabhängig vom Nachrichteninhalt bearbeiten müssen (z. B. zum Routen oder Weiterleiten von Nachrichten beim Erstellen eines Routers, Lastenausgleichsmoduls oder Veröffentlichen-Abonnieren-Systems).
Machen Sie sich vor dem Verwenden der Message-Klasse mit der WCF-Datenübertragungsarchitektur vertraut. Informationen hierzu finden Sie unter Datenübertragungsarchitektur: Übersicht.
Eine Message ist ein Allzweckcontainer für Daten, ihr Design folgt aber eng dem Design einer Nachricht im SOAP-Protokoll. Wie in SOAP verfügt eine Nachricht über einen Nachrichtentext und einen Header. Der Nachrichtentext enthält die tatsächlichen Nutzlastdaten, während die Header zusätzliche benannte Datencontainer enthalten. Die Regeln für das Lesen und Schreiben von Text und Headern sind unterschiedlich, so werden die Header immer im Arbeitsspeicher gepuffert, und der Zugriff ist beliebig oft in beliebiger Reihenfolge möglich, während der Text nur einmal gelesen und in einem Stream übertragen werden kann. Normalerweise werden in SOAP der Nachrichtentext dem SOAP-Text und die Nachrichtenheader den SOAP-Headern zugeordnet.
Verwenden der Meldungsklasse in Vorgängen
Sie können die Message-Klasse als Eingabeparameter eines Vorgangs, als den Rückgabewert eines Vorgangs oder beides verwenden. Wenn Message an einer Stelle in einem Vorgang verwendet wird, gelten die folgenden Einschränkungen:
Der Vorgang kann über keinen
out
-Parameter oderref
-Parameter verfügen.Es kann nicht mehr als einen
input
-Parameter geben. Wenn der Parameter vorhanden ist, muss er entweder eine Nachricht oder ein Nachrichtenvertragstyp sein.Der Rückgabetyp muss entweder
void
,Message
oder ein Nachrichtenvertragstyp sein.
Das folgende Codebeispiel enthält einen gültigen Vorgangsvertrag.
[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
Erstellen grundlegender Nachrichten
Die Message-Klasse stellt statische CreateMessage
-Factorymethoden bereit, mit denen Sie grundlegende Nachrichten erstellen können.
Alle CreateMessage
-Überladungen nehmen einen Versionsparameter vom Typ MessageVersion an, der die für die Nachricht zu verwendende SOAP-Version und Version der WS-Adressierung angibt. Wenn Sie die gleichen Protokollversionen wie die eingehende Nachricht verwenden möchten, können Sie die IncomingMessageVersion-Eigenschaft auf der OperationContext-Instanz nutzen, die aus der Current-Eigenschaft abgerufen wurde. Die meisten CreateMessage
-Überladungen haben außerdem einen Zeichenfolgenparameter, der die für die Nachricht zu verwendende SOAP-Aktion angibt. Die Version kann auf None
festgelegt werden, um die Generierung des SOAP-Umschlags zu deaktivieren; die Nachricht besteht nur aus dem Text.
Erstellen von Nachrichten aus Objekten
Die grundlegendste CreateMessage
-Überladung, die nur eine Version und eine Aktion annimmt, erstellt eine Nachricht, die keinen Text enthält. Eine weitere Überladung nimmt einen zusätzlichen Object-Parameter an; es wird eine Nachricht erstellt, deren Text die serialisierte Darstellung des entsprechenden Objekts ist. Verwenden Sie das DataContractSerializer mit Standardeinstellungen für die Serialisierung. Wenn Sie ein anderes Serialisierungsprogramm verwenden möchten oder das DataContractSerializer
anders konfiguriert werden soll, verwenden Sie die CreateMessage
-Überladung, die auch einen XmlObjectSerializer
-Parameter annimmt.
Sie können z. B. zum Zurückgeben eines Objekts in einer Nachricht den folgenden Code verwenden.
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
Erstellen von Nachrichten aus XML-Lesern
Bestimmte CreateMessage
-Überladungen nehmen einen XmlReader oder einen XmlDictionaryReader für den Text anstelle eines Objekts an. In diesem Fall enthält der Text der Nachricht die XML, die aus dem Lesen des übergebenen XML-Readers resultiert. Der folgende Code gibt beispielsweise eine Nachricht mit aus einer XML-Datei gelesenem Textinhalt zurück:
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
Darüber hinaus gibt es CreateMessage
-Überladungen, die einen XmlReader oder einen XmlDictionaryReader annehmen, der die gesamte Nachricht und nicht nur den Text darstellt. Diese Überladungen nehmen auch einen ganzzahligen maxSizeOfHeaders
-Parameter an. Header werden immer in den Arbeitsspeicher gepuffert, sobald die Nachricht erstellt wurde, und dieser Parameter begrenzt die stattfindende Pufferung. Dieser Parameter sollte auf einen sicheren Wert festgelegt werden, wenn die XML von einer nicht vertrauenswürdigen Quelle stammt, um einen möglichen Denial-of-Service-Angriff zu vermeiden. Die SOAP-Version und die Version der WS-Adressierung der Nachricht, die der XML-Leser darstellt, müssen mit den durch den Versionsparameter angegebenen Versionen übereinstimmen.
Erstellen von Nachrichten mit dem Body Writer-Objekt
Eine CreateMessage
-Überladung nimmt eine BodyWriter
-Instanz an, um den Text der Nachricht zu beschreiben. Ein BodyWriter
ist eine abstrakte Klasse, die abgeleitet werden kann, um das Erstellen von Nachrichtentexten anzupassen. Sie können eine eigene abgeleitete BodyWriter
-Klasse erstellen, um Nachrichtentexte benutzerdefiniert zu beschreiben. Sie müssen die BodyWriter.OnWriteBodyContents
-Methode überschreiben, die einen XmlDictionaryWriter annimmt; diese Methode ist für das Schreiben des Textes zuständig.
Body Writer-Objekte können gepuffert oder nicht gepuffert (gestreamt) sein. Gepufferte Body Writer-Objekte schreiben ihren Inhalt beliebig oft, während gestreamte Objekte den Inhalt nur einmal schreiben können. Die IsBuffered
-Eigenschaft gibt an, ob ein Body Writer-Objekt gepuffert ist oder nicht. Sie können dies für das Body Writer-Objekt festlegen, indem Sie den geschützten BodyWriter
-Konstruktor aufrufen, der einen booleschen isBuffered
-Parameter annimmt. Body Writer-Objekte unterstützen das Erstellen eines gepufferten Body Writer-Objekts aus einem nicht gepufferten Body Writer-Objekt. Sie können die OnCreateBufferedCopy
-Methode überschreiben, um diesen Prozess anzupassen. Standardmäßig wird ein Speicherpuffer verwendet, der das von OnWriteBodyContents
zurückgegebene XML enthält. OnCreateBufferedCopy
akzeptiert einen ganzzahligen maxBufferSize
-Parameter. Wenn Sie diese Methode überschreiben, dürfen Sie keine Puffer erstellen, die diese maximale Größe überschreiten.
Die BodyWriter
-Klasse bietet die WriteBodyContents
-Methode und die CreateBufferedCopy
-Methode, die im Grunde einfache Wrapper um die OnWriteBodyContents
-Methode bzw. die OnCreateBufferedCopy
-Methode sind. Diese Methoden führen eine Zustandsprüfung durch, um sicherzustellen, dass auf ein nicht gepuffertes Body Writer-Objekt höchstens einmal zugegriffen wird. Diese Methoden werden nur direkt aufgerufen, wenn benutzerdefinierte abgeleitete Message
-Klassen erstellt werden, die auf BodyWriters
basieren.
Erstellen von Fehlermeldungen
Sie können bestimmte CreateMessage
-Überladungen verwenden, um SOAP-Fehlermeldungen zu erstellen. Die grundlegendste dieser Überladungen nimmt ein MessageFault-Objekt an, das den Fehler beschreibt. Andere Überladungen werden aus Gründen der Benutzerfreundlichkeit bereitgestellt. Die erste solcher Überladungen nimmt einen FaultCode
und eine Zeichenfolge der Ursache an und erstellt einen MessageFault
mit MessageFault.CreateFault
unter Verwendung dieser Informationen. Die andere Überladung nimmt eine Detailobjekt an und übergibt es auch zusammen mit dem Fehlercode und der Ursache an CreateFault
. So gibt beispielsweise der folgende Vorgang einen Fehler zurück:
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
Extrahieren von Nachrichtentextdaten
Die Message
-Klasse unterstützt mehrere Methoden zum Extrahieren von Informationen aus dem Text. Diese können in die folgenden Kategorien klassifiziert werden:
Einmaliges Abrufen des gesamten Nachrichtentexts in einen XML-Writer. Dies wird auch als Schreiben einer Nachricht bezeichnet.
Laufenlassen eines XML-Readers über den Nachrichtentext. Dies ermöglicht es Ihnen, später nach Bedarf Stück für Stück auf den Nachrichtentext zuzugreifen. Dies wird auch als Lesen einer Nachricht bezeichnet.
Die gesamte Nachricht, einschließlich des Texts, kann in einen Puffer im Arbeitsspeicher vom MessageBuffer-Typ kopiert werden. Dies wird auch als Kopieren einer Nachricht bezeichnet.
Sie können unabhängig von der Zugriffsmethode nur einmal auf den Text einer Message
zugreifen. Ein Nachrichtenobjekt verfügt über eine State
-Eigenschaft, die anfänglich auf "Erstellt" festgelegt ist. Mit den drei zuvor beschriebenen Zugriffsmethoden wird der Zustand auf "Geschrieben", "Gelesen" bzw. "Kopiert" festgelegt. Darüber hinaus kann eine Close
-Methode den Zustand auf "Geschlossen" festlegen, wenn der Inhalt des Nachrichtentexts nicht mehr benötigt wird. Auf den Nachrichtentext kann nur im Zustand "Erstellt" zugegriffen werden, und eine Rückkehr zu diesem Zustand ist nach einer Zustandsänderung nicht mehr möglich.
Schreiben von Nachrichten
Die WriteBodyContents(XmlDictionaryWriter)-Methode schreibt den Inhalt des Texts einer bestimmten Message
-Instanz in einen angegebenen XML-Writer. Die WriteBody-Methode führt den gleichen Vorgang aus, mit dem Unterschied, dass der Textinhalt in ein geeignetes Wrapperelement (z. B. <soap:body>
) eingeschlossen wird. Schließlich schreibt WriteMessage die ganze Nachricht, einschließlich des umfassenden SOAP-Umschlags und der Header. Bei deaktiviertem SOAP (mit Version gleich MessageVersion.None) wird von allen drei Methoden der gleiche Vorgang ausgeführt: Der Inhalt des Nachrichtentexts wird geschrieben.
Beispielsweise schreibt der folgende Code der Text einer eingehenden Nachricht in eine Datei.
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
Zwei zusätzliche Hilfsmethoden schreiben bestimmte SOAP-Startelement-Tags. Diese Methoden haben keinen Zugriff auf den Nachrichtentext, der Nachrichtenzustand wird also nicht geändert. Dazu gehören:
WriteStartBody schreibt das Start-Textelement, z. B.
<soap:Body>
.WriteStartEnvelope schreibt das Start-Umschlagelement, z. B.
<soap:Envelope>
.
Rufen Sie zum Schreiben der jeweiligen Endelement-Tags WriteEndElement
auf dem entsprechenden XML-Writer auf. Diese Methoden werden selten direkt aufgerufen.
Lesen von Nachrichten
Die primäre Methode zum Lesen eines Nachrichtentexts ist das Aufrufen von GetReaderAtBodyContents. Ein XmlDictionaryReader wird zurückgegeben, mit dem Sie den Nachrichtentext lesen können. Beachten Sie, dass die Message in den Zustand "Gelesen" übergeht, sobald GetReaderAtBodyContents aufgerufen wird, und nicht, wenn Sie den XML-Reader verwenden.
Die GetBody-Methode ermöglicht Ihnen auch den Zugriff auf den Nachrichtentext als typisiertes Objekt. Intern verwendet diese Methode GetReaderAtBodyContents
, daher geht die Nachricht auch in den Read-Zustand über (siehe die State-Eigenschaft).
Es wird empfohlen, die IsEmpty-Eigenschaft zu überprüfen, wobei in diesem Fall die Nachricht keinen Text enthält und GetReaderAtBodyContents eine InvalidOperationException auslöst. Wenn es sich um eine empfangene Nachricht handelt (beispielsweise um die Antwort), sollten Sie auch IsFault überprüfen, wodurch angegeben wird, ob die Nachricht einen Fehler enthält.
Die grundlegendste Überladung von GetBody deserialisiert den Nachrichtentext in eine Instanz eines Typs (angegeben durch den generischen Parameter) unter Verwendung eines DataContractSerializer, konfiguriert mit den Standardeinstellungen und mit deaktiviertem MaxItemsInObjectGraph-Kontingent. Wenn Sie eine andere Serialisierungs-Engine verwenden oder das DataContractSerializer
nicht standardmäßig konfigurieren möchten, verwenden Sie die GetBody-Überladung, die ein XmlObjectSerializer annimmt.
Der folgende Code extrahiert beispielsweise Daten aus einem Nachrichtentext, der ein serialisiertes Person
-Objekt enthält, und gibt den Namen der Person aus.
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
Kopieren einer Nachricht in einen Puffer
Manchmal ist es erforderlich, mehrmals auf den Nachrichtentext zuzugreifen, beispielsweise um die gleiche Nachricht an mehrere Ziele als Teil eines Verleger-Abonnent-Systems weiterzuleiten. In diesem Fall ist es notwendig, die gesamte Nachricht (einschließlich des Textes) im Arbeitsspeicher zu puffern. Sie können dazu CreateBufferedCopy(Int32) aufrufen. Diese Methode nimmt einen ganzzahligen Parameter an, der die maximale Puffergröße darstellt, und erstellt einen Puffer mit maximal dieser Größe. Legen Sie dafür einen sicheren Wert fest, wenn die Nachricht von einer nicht vertrauenswürdigen Quelle stammt.
Der Puffer wird als MessageBuffer-Instanz zurückgegeben. Es gibt mehrere Möglichkeiten, um auf Daten im Puffer zuzugreifen. Die primäre Methode ist, CreateMessage aufzurufen, um Message
-Instanzen aus dem Puffer zu erstellen.
Eine weitere Methode ist das Implementieren der IXPathNavigable-Schnittstelle, die die MessageBuffer-Klasse implementiert, um direkt auf die zugrunde liegende XML zuzugreifen. Einige CreateNavigator-Überladungen ermöglichen das Erstellen von durch ein Knotenkontingent geschützten System.Xml.XPath-Navigatoren, die die Anzahl der aufrufbaren XML-Knoten beschränken. Dies hilft, Denial-of-Service-Angriffe auf der Grundlage von längerer Verarbeitungszeit zu verhindern. Dieses Kontingent ist standardmäßig deaktiviert. Mit einigen CreateNavigator
-Überladungen können Sie angeben, wie viele Leerstellen in der XML mit der XmlSpace-Enumeration behandelt werden soll, wobei der Standard XmlSpace.None
ist.
Die letzte Methode zum Zugreifen auf den Inhalt eines Nachrichtenpuffers ist das Schreiben des Inhalts in einen Stream mit WriteMessage.
Das folgende Beispiel demonstriert das Arbeiten mit einem MessageBuffer
: Eine eingehende Nachricht wird an mehrere Empfänger weitergeleitet und anschließend in einer Datei protokolliert. Ohne Pufferung ist dies nicht möglich, da dann der Zugriff auf den Nachrichtentext nur einmal möglich ist.
[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
Die MessageBuffer
-Klasse verfügt über andere erwähnenswerte Member. Die Close-Methode kann aufgerufen werden, um Ressourcen freizugeben, wenn der Pufferinhalt nicht mehr benötigt wird. Die BufferSize-Eigenschaft gibt die Größe des reservierten Puffers zurück. Die MessageContentType-Eigenschaft gibt den MIME-Inhaltstyp der Nachricht zurück.
Zugreifen auf den Nachrichtentext zum Debuggen
Zum Debuggen können Sie die ToString-Methode aufrufen, um eine Darstellung der Nachricht als Zeichenfolge abzurufen. Diese Darstellung stimmt im Allgemeinen mit der Darstellung einer Nachricht überein, wenn sie mit dem Textencoder codiert worden wäre, außer dass die XML zur Lesbarkeit besser formatiert wäre. Die einzige Ausnahme ist der Nachrichtentext. Der Text kann nur einmal gelesen werden, und ToString
ändert den Nachrichtenzustand nicht. Daher kann die ToString
-Methode möglicherweise nicht auf den Text zugreifen, und der Nachrichtentext wird durch einen Platzhalter (z. B. „…“ oder drei Punkte) ersetzt. Verwenden Sie deswegen ToString
nicht zum Protokollieren von Nachrichten, wenn der Textinhalt der Nachrichten wichtig ist.
Zugreifen auf andere Nachrichtenteile
Für den Zugriff auf andere Informationen zur Nachricht als den Textinhalt werden verschiedene Eigenschaften bereitgestellt. Diese können jedoch nicht aufgerufen werden, wenn die Nachricht geschlossen wurde:
Die Headers-Eigenschaft stellt die Nachrichtenheader dar. Weitere Informationen finden Sie weiter unten im Abschnitt „Arbeiten mit Headern“.
Die Properties-Eigenschaft stellt die Eigenschaften der Nachricht dar, die Teile an die Nachricht angehängter benannter Daten sind, die beim Senden der Nachricht im Allgemeinen nicht ausgegeben werden. Weitere Informationen finden Sie im Abschnitt "Arbeiten mit Eigenschaften" weiter unten in diesem Thema.
Die Version-Eigenschaft gibt die der Nachricht zugeordnete SOAP-Version und Version der WS-Adressierung an, oder
None
, wenn SOAP deaktiviert ist.Die IsFault-Eigenschaft gibt
true
zurück, wenn die Nachricht eine SOAP-Fehlermeldung ist.Die IsEmpty-Eigenschaft gibt
true
zurück, wenn die Nachricht keinen Text enthält.
Sie können mithilfe der GetBodyAttribute(String, String)-Methode auf ein bestimmtes, mit einem bestimmten Namen und Namespace bezeichnetes Attribut im Textwrapperelement (z. B. <soap:Body>
) zugreifen. Wenn ein solches Attribut nicht gefunden wurde, wird null
zurückgegeben. Diese Methode kann nur dann aufgerufen werden, wenn sich die Message
im Zustand "Erstellt" befindet (wenn noch nicht auf den Nachrichtentext zugegriffen wurde).
Arbeiten mit Headern
Message
kann eine beliebige Anzahl benannter XML-Fragmente enthalten, die als Header bezeichnet werden. Jedes Fragment wird normalerweise einem SOAP-Header zugeordnet. Der Zugriff auf Header erfolgt über die Headers
-Eigenschaft des Typs MessageHeaders. MessageHeaders ist eine Auflistung von MessageHeaderInfo-Objekten, und auf einzelne Header kann über die IEnumerable-Schnittstelle oder durch den Indexer zugegriffen werden. Im folgenden Code werden z. B. die Namen aller Header in einer Message
aufgelistet.
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
Hinzufügen, Entfernen und Suchen von Headern
Sie können einen neuen Header am Ende aller vorhandenen Header mit der Add-Methode hinzufügen. Sie können die Insert-Methode verwenden, um einen Header an einem bestimmten Index einzufügen. Vorhandene Header werden für das eingefügte Element verschoben. Header werden nach ihrem Index geordnet, dabei ist der erste verfügbare Index 0. Sie können mithilfe der verschiedenen CopyHeadersFrom-Methodenüberladungen Header aus einer anderen Message
- oder MessageHeaders
-Instanz hinzufügen. Einige Überladungen kopieren einen einzelnen Header, während andere alle Header kopieren. Mit der Clear-Methode werden alle Header entfernt. Die RemoveAt-Methode entfernt einen Header an einem bestimmten Index (alle nachfolgenden Header werden verschoben). Die RemoveAll-Methode entfernt alle Header mit einem bestimmten Namen und Namespace.
Rufen Sie mit der FindHeader-Methode einen bestimmten Header ab. Diese Methode nimmt den Namen und Namespace des zu suchenden Headers an, und gibt seinen Index zurück. Wenn der Header mehrmals auftritt, wird eine Ausnahme ausgelöst. Wenn der Header nicht gefunden wurde, wird -1 zurückgegeben.
Im SOAP-Headermodell können Header einen Actor
-Wert aufweisen, der den gewünschten Empfänger des Headers angibt. Die grundlegendste FindHeader
-Überladung sucht nur Header, die für den letzten Empfänger der Nachricht vorgesehen sind. Mit einer anderen Überladung können Sie jedoch angeben, welche Actor
-Werte in der Suche enthalten sind. Weitere Informationen finden Sie in der SOAP-Spezifikation.
Eine CopyTo(MessageHeaderInfo[], Int32)-Methode wird für das Kopieren von Headern aus einer MessageHeaders-Auflistung zu einem Array von MessageHeaderInfo-Objekten bereitgestellt.
Für den Zugriff auf die XML-Daten in einem Header können Sie GetReaderAtHeader aufrufen und einen XML-Leser für den Headerindex zurückgeben. Verwenden Sie zum Deserialisieren des Headerinhalts in ein Objekt GetHeader<T>(Int32) oder eine der anderen Überladungen. Die grundlegendsten Überladungen deserialisieren Header mit dem DataContractSerializer, der standardmäßig konfiguriert wurde. Wenn Sie ein anderes Serialisierungsprogramm oder eine andere Konfiguration von DataContractSerializer
verwenden möchten, nutzen Sie eine der Überladungen, die ein XmlObjectSerializer
akzeptieren. Einige Überladungen akzeptieren auch den Headernamen, Namespace und optional eine Liste von Actor
-Werte anstelle eines Index; dies ist eine Kombination von FindHeader
und GetHeader
.
Arbeiten mit Eigenschaften
Eine Message
-Instanz kann eine beliebige Anzahl von benannten Objekten beliebiger Typen enthalten. Auf diese Auflistung wird über die Properties
-Eigenschaft vom Typ MessageProperties
zugegriffen. Die Auflistung implementiert die IDictionary<TKey,TValue>-Schnittstelle und fungiert als Zuordnung von String zu Object. Normalerweise werden Eigenschaftswerte nicht direkt einem Teil der Nachricht zugeordnet, sondern bieten vielmehr verschiedene Nachrichtenverarbeitungshinweise für die Kanäle im WCF-Kanalstapel oder für das CopyTo(MessageHeaderInfo[], Int32)-Dienstframework. Ein Beispiel finden Sie unter Datenübertragungsarchitektur: Übersicht.
Erben von der Meldungsklasse
Erstellen Sie eine von der CreateMessage
-Klasse abgeleitete Klasse, wenn die mit Message
erstellten integrierten Nachrichtentypen nicht den Anforderungen entsprechen.
Definieren des Nachrichtentextinhalts
Für den Zugriff auf Daten innerhalb eines Nachrichtentexts gibt es drei grundlegende Verfahren: Schreiben, Lesen und Kopieren in einen Puffer. Diese Verfahren führen im Endeffekt dazu, dass die OnWriteBodyContents-Methode, die OnGetReaderAtBodyContents-Methode bzw. die OnCreateBufferedCopy-Methode in der abgeleiteten Message
-Klasse aufgerufen wird. Die Message
-Basisklasse stellt sicher, dass für jede Message
-Instanz nur eine dieser Methoden aufgerufen wird und dieser Vorgang jeweils nur einmal stattfindet. Die Basisklasse stellt auch sicher, dass die Methoden nicht für eine geschlossene Nachricht aufgerufen werden. Es ist nicht erforderlich, den Nachrichtenzustand in der Implementierung zu verfolgen.
OnWriteBodyContents ist eine abstrakte Methode und muss implementiert werden. Die grundlegendste Weise zum Definieren des Textinhalts der Nachricht ist, mit dieser Methode zu schreiben. Zum Beispiel enthält die folgende Nachricht 100.000 Zufallszahlen von 1 bis 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.
Die OnGetReaderAtBodyContents()-Methode und die OnCreateBufferedCopy-Methode verfügen über Standardimplementierungen, die in den meisten Fällen funktionieren. Die Standardimplementierungen rufen OnWriteBodyContents auf, speichern die Ergebnisse im Puffer und arbeiten mit dem resultierenden Puffer. In einigen Fällen genügt dies möglicherweise nicht. In vorhergehenden Beispiel führt das Lesen der Nachricht dazu, dass 100.000 XML-Elemente im Puffer gespeichert werden, was unter Umständen nicht wünschenswert ist. Es kann empfehlenswert sein, OnGetReaderAtBodyContents() zu überschreiben, um eine benutzerdefinierte abgeleitete XmlDictionaryReaderKlasse zurückzugeben, die Zufallszahlen bereitstellt. Sie können dann OnWriteBodyContents überschreiben, um den von der OnGetReaderAtBodyContents()-Methode zurückgegebenen Reader, wie im folgenden Beispiel dargestellt, zu verwenden.
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
Auf ähnliche Weise können Sie OnCreateBufferedCopy
überschreiben, um eine eigene abgeleitete MessageBuffer
-Klasse zurückzugeben.
Zusätzlich zum Bereitstellen von Nachrichtentextinhalt muss die abgeleitete Nachrichtenklasse auch die Version
-Eigenschaft, die Headers
-Eigenschaft und die Properties
-Eigenschaft überschreiben.
Beachten Sie, dass beim Erstellen der Kopie einer Nachricht die Kopie die Nachrichtenheader aus dem Original verwendet.
Andere Member, die überschrieben werden können
Sie können die Methoden OnWriteStartEnvelope, OnWriteStartHeaders und OnWriteStartBody überschreiben, um anzugeben, wie der SOAP-Umschlag, die SOAP-Header und die Starttags der SOAP-Textelemente geschrieben werden. Diese entsprechen in der Regel <soap:Envelope>
, <soap:Header>
und <soap:Body>
. Diese Methoden sollten normalerweise nichts schreiben, wenn die Version-Eigenschaft None zurückgibt.
Hinweis
Die Standardimplementierung von OnGetReaderAtBodyContents
ruft OnWriteStartEnvelope
und OnWriteStartBody
auf, bevor OnWriteBodyContents
aufgerufen wird und die Ergebnisse im Puffer gespeichert werden. Header werden nicht geschrieben.
Überschreiben Sie die OnWriteMessage-Methode, um das Verfahren zu ändern, wie die gesamte Nachricht aus den verschiedenen Teilen erstellt wird. Die OnWriteMessage
-Methode wird aus WriteMessage und aus der OnCreateBufferedCopy-Standardimplementierung aufgerufen. Beachten Sie, dass das Überschreiben von WriteMessage keine empfohlene Vorgehensweise ist. Es ist besser, die entsprechenden On
-Methoden zu überschreiben, z. B OnWriteStartEnvelope, OnWriteStartHeaders und OnWriteBodyContents.
Überschreiben Sie OnBodyToString, um zu überschreiben, wie der Nachrichtentext während des Debuggens dargestellt wird. Der Standard ist die Darstellung als drei Punkte ("…"). Diese Methode kann mehrfach ausgerufen werden, wenn der Nachrichtenzustand nicht "Geschlossen" lautet. Eine Implementierung dieser Methode sollte nie eine Aktion verursachen, die nur einmal ausgeführt werden muss (wie das Lesen eines Vorwärtsstreams).
Überschreiben Sie die OnGetBodyAttribute-Methode, um den Zugriff auf Attribute im SOAP-Textelement zuzulassen. Diese Methode kann beliebig oft aufgerufen werden, der Message
-Basistyp stellt jedoch sicher, dass sie nur dann aufgerufen wird, wenn der Zustand der Nachricht "Erstellt" lautet. Es ist nicht erforderlich, den Zustand in einer Implementierung zu überprüfen. Die Standardimplementierung gibt immer null
zurück, was bedeutet, dass im Textelement keine Attribute vorhanden sind.
Soll das Message
-Objekt eine spezielle Bereinigung ausführen, wenn der Nachrichtentext nicht mehr benötigt wird, können Sie OnClose überschreiben. Bei der Standardimplementierung wird keine Aktion ausgeführt.
Die IsEmpty
-Eigenschaft und die IsFault
-Eigenschaft können überschrieben werden. Standardmäßig geben beide false
zurück.