.NET

Linq to XML und die Microsoft Message Queue

Der MSMQ ein XDocument unterschieben

Die Micosoft Message Queue ist eine feine Sache zur asynchronen Verarbeitung von Daten. Die .NET-Implementierung im Namensraum System.Messaging kennt dabei zwei hauptsächtliche Objekte: MessageQueue und Message. Ersteres muss man öffnen, um letzteres absetzen zu können.

Die Nachrichten, die eine Message beinhalten kann, werden über sog. Formatter in ein für die Gegenstelle lesbares Format gebracht. In .NET ist der Standard-Formatter der XmlMessageFormatter, der beliebige, serialisierbare Objekte in XML und dann in ein Stream-Objekt umsetzt, dass an die Message-Queue verschickt wird.

Eine Beispiel-Implementierung mit zwei Message-Queues (eine zum schreiben und eine zum lesen) könnte folgendermaßen aussehen:

'*** Neue In-Queue
Dim mqIn As New MessageQueue(".\private$\my_in_queue")
'(Festlegen, dass eine ID benötigt wird)
mqIn.MessageReadPropertyFilter.CorrelationId = True

'*** Neue Out-Queue
Dim mqOut As New MessageQueue(".\private$\my_out_queue")

'*** Nachricht versenden
Dim msgIn As New Message(MyObject)
'(Antwort-Queue festlegen)
msgIn.ResponseQueue = mqOut
'(Formatter festlegen und Typ bekannt machen)
msgIn.Formatter = New XmlMessageFormatter(New Type() {GetType(MyObject)})
'(Nachricht senden)
mqIn.Send(msgIn)

'*** Antwort empfangen
Dim msgOut As New Message
Try
    '(max. 30 Sekunden nach der Antwort auf die Nachricht suchen)
    msgOut = mqOut.PeekByCorrelationId( _
	msgIn.Id, _
	New TimeSpan(0, 0, 30))
Catch ex As Exception
    '... Fehler "Server antwortet nicht" verarbeiten
End Try

Dim obj As MyObject
Try
    '(Nachricht abrufen)
    msgOut = mqOut.ReceiveById(msgOut.Id)
    '(Formatter festlegen und Typ bekannt machen)
    msgOut.Formatter = New XmlMessageFormatter(New Type() {GetType(MyObject)})
    '(Objekt abrufen)
    obj = msgOut.Body
Catch ex As Exception
    '... Fehler verarbeiten
End Try

Das sieht nett aus, oder? Man nehme ein x-beliebiges, serialisierbares Objekt wie MyObject und werfe es der Queue zum Frass vor. Der Formatter kümmert sich selbständig um die Konvertierung und wir haben nichts weiter zu tun.

Das Problem

Meine Aufgabenstellung kürzlich war allerdings etwas anders. Es ging um kleine XML-Schnipsel, die zwischen einem Navision-Server und meinem .NET-Code ausgetauscht werden sollten. Für jede einzelne Anweisung extra Objekte zu erzeugen, schien mir etwas zu aufwendig, also entschied ich mich, den XML-Code über Linq to XML zu erzeugen und direkt in die Queue einzustellen.

Dim xMsg As XElement = 
	<data>
		<property_1>Test</property_1>
		<property_2>Noch ein Test</property_2>
	</data>
...

Dim msgIn As New Message(xMsg.ToString())

...

Was nun passierte war, das der XMLFormatter beim Senden der Nachricht anlief und mir brav meinen XML-Code in XML packte, d.h. er ersetzte die spitzen Klammern durch HTML-Entities und umschloss den Text mit <string></string>. Irgendwie hatte er ja recht, denn String ist ein Objekt und so verpackt man Text vernünftig in XML.

Mit Erstauen stellte ich dann fest, dass Microsoft nicht daran gedacht hatte, dass man bei der Verwendung der Message-Queue vielleicht bereits über das nötige XML verfügt! So sinnvoll die drei veschiedenen Formatter (XmlMessageFormatter, BinaryMessageFormatter und ActiveXMessageFormatter) auch sind, einfach XML einstellen is' nicht...

Da alle Formatter schlußendlich nicht anderes machen als den Wert der Eigenschaft Message.Body in einen Stream umzuwandeln (in die Eigenschaft Message.BodyStream), habe ich mir für die Typen XDocument und XElement einen eigenen Formatter geschrieben, der von IMessageFormatter ableitet und den Inhalt direkt in einen Stream umwandelt.

Die wichtigsten Methoden sind hierbei Write und Read:

Public Sub Write(ByVal message As Message, ByVal obj As Object) _
	Implements IMessageFormatter.Write

	If message Is Nothing Then
	    Throw New System.ArgumentNullException("message")
	End If

	Dim xd As New XDocument

	If TypeOf message.Body Is XDocument Then
	    xd = message.Body
	ElseIf TypeOf message.Body Is XElement Then
	    xd = New XDocument(message.Body)
	End If

	Dim ms As New MemoryStream
	Dim sw As New StreamWriter(ms)
	xd.Save(sw)
	sw.Flush()

	message.BodyStream = ms
	message.BodyType = 0

End Sub

Public Function Read(ByVal message As Message) As Object _
	Implements IMessageFormatter.Read

	If message Is Nothing Then
	    Throw New System.ArgumentNullException("message")
	End If

	Dim sr As New StreamReader(message.BodyStream)
	Dim xr As XmlReader = XmlReader.Create(sr)
	Dim xd As XDocument = XDocument.Load(xr)

	Return xd

End Function

Beim Schreiben einer Nachricht wird das XDocument in einen StreamWriter gespeichert und direkt in den BodyStream geschrieben und beim Lesen wird Letzterer über einen StreamReader in einen XMLReader umgewandelt, mit dem das auszugebende XDocument erzeugt wird.

Obiges Beispiel muss für den Einsatz von XLinq nur minimal angepasst werden:

Dim xMsg As XElement = <data>
					      <property_1>Test</property_1>
						  <property_2>Noch ein Test</property_2>
					   </data>
...

Dim msgIn As New Message(xMsg)
msgIn.Formatter = New XLinqMessageFormatter

...

msgOut = mqOut.ReceiveById(msgOut.Id)
msgOut.Formatter = New XLinqMessageFormatter
obj = msgOut.Body

...

Happy queueing ;)

Downloads

XLinqMessageFormatter.vb.txt
kick it on dotnet-kicks.de AddThis 0 wikio-Stimme(n) Trackback-Url...

Keine Kommentare bislang...

Dein Kommentar hierzu...


Kommentar-Feed für diesen Beitrag
Gravatare werden unterstützt .:. eMail-Adressen werden nicht veröffentlicht
 

RSS-Feed

Die URL des Standard-Newsfeed von zerbit.de lautet:

http://www.zerbit.de/rssfeed.aspx

Login


 

 

Statistik



kürzlich kommentiert

Artikel 292

  • Datum: 19.08.2009
    Kategorie: .NET
    Zugriffe: 4.830
    Kommentare: 0
    Trackbacks: 0

Letzte Beiträge

Kategorien

Buttons & More

Blog-Roll

Banner Piraten-Partei