4

给定一个类:

[DataContract]
public sealed class ChangedField
{
    [DataMember(Name="I")] public ushort FieldId { get; set; }
    [DataMember(Name="V")] public object Value { get; set; }
}

WireShark 显示,当通过 WCF TCP 绑定发送时,消息的编码是二进制的(仅限可打印字符,但你明白了):

ChangedFielda.I..a.V....e:double..e http://www.w3.org/2001/XMLSchema.s.....a.

但是如果我像这样序列化这种类型的实例......

var ser = new DataContractSerializer(typeof(ChangedField));
var stream = new MemoryStream();
ser.WriteObject(stream, new ChangedField { FieldId = 1, Value = 1.23d });

...然后流包含类似于以下内容的 SOAP XML:

<ChangedField>
  <I>1</I>
  <V i:type="a:double" xmlns:a="http://www.w3.org/2001/XMLSchema">1.23</V>
</ChangedField>

所以我的问题是如何控制DataContractSerializer在我自己的代码中生成这种二进制表示?

作为旁白:

如您所见,消息因object属性必须对其类型进行编码(因此是 URI)而变得臃肿。我将对其进行更改以使用自定义二进制编码,因为在我的场景中,字段 ID 确定类型(在本例中为double)。

4

4 回答 4

3

默认情况下,TCP 绑定使用二进制消息编码器,而在第二个示例中,您只是将数据协定序列化为 XML。二进制消息编码器发生的情况是,它基本上为数据协定序列化程序提供了一个自定义 XmlWriter 实现,该实现生成专有的二进制格式,而不是 XML。

如果您想将此与不同的绑定(例如 HTTP)一起使用,则需要创建自定义绑定并添加BinaryMessageEncodingElement元素而不是普通的 TextMessageEncodingElement。

于 2009-07-02T12:38:33.100 回答
1

您无法控制 DataContractSerializer - 但您可以在 WCF 绑定中控制它将序列化为文本格式还是二进制。

正如您自己注意到的,NetTcpBinding 默认为二进制,因此非常紧凑和快速。出于互操作性的考虑,基于 Http 的绑定默认为 Text,因此更冗长且性能更低。

但是没有什么能阻止您创建自己的基于二进制的 HTTP 绑定 -如果您愿意接受与外部客户端的互操作性是不可能的。这仅适用于您自己的使用相同自定义绑定的内部客户端。

基本上,您需要做的是从头开始构建您自己的自定义绑定 - 在代码或配置中。在配置中非常简单:

<system.serviceModel>
  <bindings>
     <customBinding name="binaryHttpBinding">
        <binaryMessageEncoding />
        <httpTransport />
     </customBinding>
  </bindings>
</system.serviceModel>

您必须指定的两个最小部分是消息编码和传输协议 - 其他任何内容都是可选的。

现在,您可以指定 WCF 服务和客户端,以便它们使用这个新的自定义二进制 HTTP 绑定:

<system.serviceModel>
   <services>
      <service name="YourService">
         <endpoint address="http://whatever.com:8888/YourAddress"
                   binding=binaryHttpBinding"
                   contract="IYourContract" />
      </service>
   </services>
</system.serviceModel>

(当然,这也适用于本<client>节中的客户)。

有了它——这不是以任何方式、形状或形式控制代码中的 DataContractSerializer 的问题——只需创建一个自定义绑定并使用它。

马克

编辑(留下评论后):
嗯,WCF 确实是非常可扩展的,所以你可以像消息检查器一样在离开客户端时这样做。您可以编写这样一个消息检查器(从IClientMessageInspector派生),它只检查消息并测量其大小并将其写入文件或数据库

于 2009-07-02T15:12:21.540 回答
0

您是否阅读过DataContractSerializer上的文档?

要使用 DataContractSerializer,首先创建一个类的实例和一个适合写入或读取格式的对象;例如,XmlDictionaryWriter 的一个实例。然后调用 WriteObject 方法来持久化数据。要检索数据,请创建一个适合读取数据格式的对象(例如用于 XML 文档的 XmlDictionaryReader)并调用 ReadObject 方法。

然后,您将需要阅读有关XmlDictionaryWriter的信息。

于 2009-07-02T12:36:17.627 回答
0

这是我能找到的最简单的解决方案:

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();
}

...其中encoding参数是:

new TextMessageEncodingBindingElement()

...或者...

new BinaryMessageEncodingBindingElement()

...取决于您需要文本格式还是二进制格式。

感谢tomasr提供了指向他关于此主题的文章的链接

于 2009-07-30T14:06:38.573 回答