WCF分布式开发常见错误(30):Start element 'Binary' expected(期望的初始元素是'Binary' ). Found 'SayHello'.
调试WCF4.0代码遇到的错误,目前网络上参考的资料很少,我把这个异常的信息给收集起来,包括解决办法,整理为一篇文章,供大家参考。这个问题目前没什么参考资料。使用Google也搜索不到相关的英文帮资料。分享出来,应该对大家有点参考价值。
【1】错误描述:
这个问题是在调试今天我在调试WCF自定义绑定实现字节流编码ByteStreamMessageEncoding程序例子代码的时候遇到这个错误。客户端抛出的异常是:Start element 'Binary' expected(期望的初始元素是'Binary' ). Found 'SayHello'.
绑定的定义如下:
//
创建自定义绑定
ByteStreamMessageEncodingBindingElement byteStream
=
new
ByteStreamMessageEncodingBindingElement();
//
TcpTransportBindingElement transport = new TcpTransportBindingElement();
HttpTransportBindingElement transport
=
new
HttpTransportBindingElement();
transport.AuthenticationScheme
=
System.Net.AuthenticationSchemes.Anonymous;
transport.HostNameComparisonMode
=
HostNameComparisonMode.StrongWildcard;
CustomBinding binding
=
new
CustomBinding(byteStream, transport);
操作的定义如下:
//
2.服务类,继承接口。实现服务契约定义的操作
public
class
WCFService : IWCFService
{
//
实现接口定义的方法
public
void
SayHello(
byte
[] b)
{
Console.WriteLine(
"
Hello! {0},Calling at {1} ...
"
, b,DateTime.Now.ToLongTimeString());
//
return b;
}
}
【2】错误截图:
运行程序,客户端调用服务操作,抛出的异常截图如下:
【3】问题分析:
这个问题应该是由于消息的根节点元素不正确导致的。这里的SOAP消息的Body默认根节点应该是<SayHello>。
SOAP消息不符合Schema导致的。WCF里提供了能够控制消息序列化的机制就是使用Message类型来控制序列化。
【4】解决办法:
首先我们要重新定义一下操作,这里服务契约不能定义为普通的契约,而要使用消息契约来代替数据契约。
声明如下:
//
1.服务契约
[ServiceContract]
public
interface
IWCFService
{
//
操作契约
[OperationContract(Action
=
"
*
"
, ReplyAction
=
"
*
"
)]
Message UpLoad(Message request);
}
其次控制Message对象,这是客户端的请求消息Body,第一个根节点必须为Binary节点。
这里比较复杂的一点是要了解WCF的消息序列化机制。在《WCF技术内幕》有过介绍。我们在实例化Message对象的时候,要提供一个BodyWriter的之类对象,它可以负责Message对象的消息体部分的处理工作。我们可以控制节点的添加。
BodyWriter为抽象类,我们要定义一个类型继承实现它的抽象方法。定义如下:
class
ByteStreamBodyWriter : BodyWriter
{
string
testFileName;
public
ByteStreamBodyWriter(
string
testFileName)
:
base
(
false
)
{
this
.testFileName
=
testFileName;
}
protected
override
void
OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement(
"
Binary
"
);
//
写数据
writer.WriteEndElement();
fs.Close();
}
}
OnWriteBodyContents(XmlDictionaryWriter writer)方法里提供了一个消息体元素"Binary"的定义,我们把数据放到这个节点下面,就可以解决问题。这个时候请求消息里就会包含需要的节点。在此启动程序基本正常。
【5】总结:
(1)这里比较值得注意的问题是,根据提示我曾经尝试把方法名字修改为"Binary"。
这个做法基于的出发点就是WCF会把方法名默认作为消息的根节点名。但是后来尝试失败。应该是SOAP消息里包含了新的后缀,我很难控制消息体的根节点元素名称。
(2)另外就是ByteStreamMessageEncodingBindingElement的使用只能是基于自定义绑定,而且对方要求把数据放到消息里的"Binary"节点里:
<
Binary
>字节流
数据
</
Binary
>
这里唯一的办法就是使用Message契约来实现,WCF提供的可以控制消息体的方法也就是通过控制SOAP消息的序列化过程。这里我们使用的一个重要类型BodyWriter。它是一个抽象类,是许多消息体处理类型的基类型。
参考资料:
1. BodyWriter Class
2.《WCF技术内幕》绑定