WCF - Mess ungefähre Nachrichtengrößen programmatisch
-
21-08-2019 - |
Frage
Bei einer Klasse:
[DataContract]
public sealed class ChangedField
{
[DataMember(Name="I")] public ushort FieldId { get; set; }
[DataMember(Name="V")] public object Value { get; set; }
}
Wireshark zeigt, dass, wenn sie über eine WCF TCP-Bindung, die Codierung der Nachricht in binär gesendet (druckbare Zeichen nur, aber Sie erhalten die Idee):
ChangedFielda.I..a.V....e:double..e http://www.w3.org/2001/XMLSchema.s.....a.
Aber wenn ich eine Instanz dieses Typs serialise wie so ...
var ser = new DataContractSerializer(typeof(ChangedField));
var stream = new MemoryStream();
ser.WriteObject(stream, new ChangedField { FieldId = 1, Value = 1.23d });
... dann enthält der Strom SOAP XML ähnelt dies:
<ChangedField>
<I>1</I>
<V i:type="a:double" xmlns:a="http://www.w3.org/2001/XMLSchema">1.23</V>
</ChangedField>
Also meine Frage ist, wie kann ich steuern DataContractSerializer
diese binäre Darstellung in meinem eigenen Code zu produzieren?
Als Nebenwirkung:
Wie Sie sehen können, wird die Nachricht durch die Tatsache, dass die aufgeblähten object
Eigenschaft seiner Art (daher die URI) codiert hat. Ich werde dies eine benutzerdefinierter binäre Codierung verwenden, um zu ändern, wie es in meinem Szenario, das Feld ID die Art bestimmt (in diesem Fall double
).
Lösung
die TCP-Bindung verwendet die binäre Nachricht Encoder standardmäßig wo, wie Sie nur den Datenvertrag in XML in Ihrem zweiten Beispiel Serialisierung. Was mit dem binären Nachricht Encoder passiert ist, dass es im Grunde den Datenvertrag Serializer mit einer benutzerdefinierten XmlWriter Implementierung bereitstellt, die ein proprietären Binärformat erzeugt, anstelle von XML.
Wenn Sie dies mit einer anderen Bindung verwenden möchten (beispielsweise HTTP), dann müssen Sie eine benutzerdefinierte erstellen Bindung statt und fügen Sie die BinaryMessageEncodingElement Element anstelle des normalen TextMessageEncodingElement.
Andere Tipps
Sie können nicht die DataContractSerializer steuern -. Sie können jedoch in Ihrer WCF steuern verbindlich, ob es in Textformat oder binär serialisiert werden
Wie Sie selbst bemerkt haben, NetTcpBinding standardmäßig binär und daher ist sehr kompakt und schnell. Der Http-basierten Bindungen Standard für die Interoperabilität willen Text und deshalb ist ausführlicher und weniger performant.
Aber es gibt nichts mehr im Weg steht Ihre eigene binäre-basierte HTTP-Erstellung Bindung - IF Sie sind bereit, zu akzeptieren, dass die Interoperabilität mit externen Kunden aus ist das Fenster, wenn Sie das tun. Dies wird nur mit Ihren eigenen internen Kunden arbeiten, die die gleichen benutzerdefinierte Bindung verwenden.
Im Grunde, was Sie tun müssen, ist Ihre eigenen von Grund auf Bindung aufzubauen - in Code oder Config. In Config ist es ziemlich einfach:
<system.serviceModel>
<bindings>
<customBinding name="binaryHttpBinding">
<binaryMessageEncoding />
<httpTransport />
</customBinding>
</bindings>
</system.serviceModel>
Die beiden Mindest Teile Sie müssen angeben sind ein Nachrichtencodierung und ein Transportprotokoll -. Alles andere ist optional
Jetzt können Sie Ihre WCF-Dienst und Client festlegen, damit sie diese neue verwenden werden, benutzerdefinierte binary-over-HTTP-Bindung:
<system.serviceModel>
<services>
<service name="YourService">
<endpoint address="http://whatever.com:8888/YourAddress"
binding=binaryHttpBinding"
contract="IYourContract" />
</service>
</services>
</system.serviceModel>
(und das gleiche funktioniert auch für die Kunden im <client>
Abschnitt, natürlich).
Da haben Sie es - es ist nicht eine Frage des DataContractSerializer in Ihrem Code in irgendeiner Weise oder Form zu steuern -. Erstellen Sie einfach eine benutzerdefinierte Bindung und es verwenden,
Marc
Bearbeiten (nach Kommentar links):
Nun, WCF ist in der Tat sehr dehnbar, so dass Sie auf dem Weg, um diese wie Nachrichteninspektoren tun aus dem Client. Sie könnten eine solche Nachricht Inspektor (abgeleitet von IClientMessageInspector
Haben Sie lesen Sie die Dokumentation auf der DataContractSerializer
die DataContractSerializer zu verwenden, zuerst eine Instanz einer Klasse erstellen und ein Objekt angemessen Schreib oder das Lesen des Formats; zum Beispiel ein Instanz des XmlDictionaryWriter. Dann rufen Sie die Writeobject-Methode beharren die Daten. Zum Abrufen von Daten, Erstellen eines Objekts angemessen Lesen der Datenformat (beispielsweise ein XmlDictionaryReader für eine XML Dokument) und rufen Sie die Readobject Verfahren.
Sie werden dann über XmlDictionaryWriter .
Hier ist die einfachste Lösung, die ich für das Problem gefunden:
static byte[] Serialise(object obj, MessageEncodingBindingElement encoding)
{
encoding.MessageVersion = MessageVersion.Soap12;
var stream = new MemoryStream();
var message = Message.CreateMessage(MessageVersion.Soap12, "", obj,
new DataContractSerializer(obj.GetType()));
encoding.CreateMessageEncoderFactory().Encoder.WriteMessage(message, stream);
return stream.ToArray();
}
... wo der encoding
Parameter ist entweder:
new TextMessageEncodingBindingElement()
... oder ...
new BinaryMessageEncodingBindingElement()
... je nachdem, ob Sie Text oder Binär-Formatierung.
Dank tomasr für die Link zu seinem Beitrag zu diesem Thema .