您的位置:首页 > 其它

[WCF 学习笔记] 8. 数据传输

2009-01-17 15:05 465 查看
WCF 服务方法通过接收消息、处理消息、回复消息来完成调用。所有的数据都被序列化,并转换成消息后再行传输,接收方执行相反的动作获得数据对象。

消息描述

最简单就是消息描述就是方法参数,所有的基本类型可以直接被序列化。我们还可以使用 MessageParameterAttribute 为参数定义消息名称。

[ServiceContract]
public interface IContract
{
[OperationContract]
double Add(double a, double b);

[OperationContract]
void Test([MessageParameter(Name="myString")]string s);
}

对于自定义类型,我们可以使用 DataContractAttribute 或 MessageContractAttribute 来定义,这些在前面的章节已经提过,此处不再做进一步说明。

WCF 服务方法支持 ref / out 关键字,也就是说底层引擎会重新为添加了关键字的对象赋予返回值。我们使用 "Message Logging" 和 "Service Trace Viewer" 查看一下 Reply Message。

Server.cs

[ServiceContract]
public interface IContract
{
[OperationContract]
double Add(double a, ref double b);
}

public class MyService : IContract
{
public double Add(double a, ref double b)
{
b += 2;
return a + b;
}
}

client.cs

using (ContractClient client = new ContractClient(new BasicHttpBinding(),
new EndpointAddress("http://localhost:8080/myservice")))
{
double b = 2;
double c = client.Add(1, ref b);
Console.WriteLine("c={0};b={1}", c, b);
}

Reply Message

<MessageLogTraceRecord>
<s:Envelope xmlns:s="http://...">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://...">http://tempuri.org/IContract/AddResponse</Action>
</s:Header>
<s:Body>
<AddResponse xmlns="http://tempuri.org/">
<AddResult>5</AddResult>
<b>4</b>
</AddResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

在 Reply Message 中除了返回值 "AddResult" 外,还有 "b"。:-)

需要注意的是,即便我们使用引用类型的参数,由于 WCF 采取序列化传送,因此它是一种 "值传递",而不是我们习惯的 "引用传递"。看看下面的例子,注意方法参数和数据结果的不同。

Server.cs

[DataContract]
public class Data
{
[DataMember]
public int I;
}

[ServiceContract]
public interface IContract
{
[OperationContract]
void Add(Data d);

[OperationContract]
void Add2(ref Data d);
}

public class MyService : IContract
{
public void Add(Data d)
{
d.I += 10;
}

public void Add2(ref Data d)
{
d.I += 10;
}
}

Client.cs

using (ContractClient client = new ContractClient(new BasicHttpBinding(),
new EndpointAddress("http://localhost:8080/myservice")))
{
Data d = new Data();
d.I = 1;
client.Add(d);
Console.WriteLine("d.I={0}", d.I);

Data d2 = new Data();
d2.I = 1;
client.Add2(ref d2);
Console.WriteLine("d2.I={0}", d2.I);
}

输出:
d.I=1
d2.I=11

序列化引擎


省情况下,WCF 使用 DataContractSerializer 引擎对相关参数进行序列化,这也是 WCF 推荐的方式。另外一个选择是
XmlSerializer,也就是 ASP.NET Web Service 所使用的序列化引擎。XmlSerializer 仅支持
DataContractSerializer 所支持的部分类型,但它允许你使用 XmlAttributeAttribute
等特性对序列化生成的 XML 进行更多的控制。

DataContractSerializer 支持的类型:

支持所有的基本类型,还包括 XmlElement 和 DateTime 这样的常用类型。

支持使用 DataContractAttribute 标记的类型。

支持使用 SerializableAttribute 标记或者实现 ISerializable 接口的类型。

实现 IXmlSerializable 接口的类型。

大多数集合(含泛型)类型,包括常用的 Array、List、IList 等。


过向服务添加 XmlSerializerFormatAttribute 标记,就可以手动切换到 XmlSerializer
引擎。此时使用自定义类型,无须使用
SerializableAttribute、ISerializable、DataContractAttribute 等特性或接口。

public class Data
{
public int I = 1234;
public string X = "Hello, World!";
}

[ServiceContract, XmlSerializerFormat]
public interface IContract
{
[OperationContract]
void Test(Data d);
}

public class MyService : IContract
{
public void Test(Data d)
{
Console.WriteLine("i={0}; x={1}", d.I, d.X);
}
}

public class WcfTest
{
public static void Test()
{
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
ServiceHost host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IContract), new BasicHttpBinding(),
"http://localhost:8080/myservice");

host.Open();
});

ChannelFactory<IContract> factory = new ChannelFactory<IContract>(new BasicHttpBinding(),
"http://localhost:8080/myservice");
IContract client = factory.CreateChannel();

Data d = new Data();
d.I = 123456;
d.X = "China";
client.Test(d);
}
}

我们为自定义类型添加控制标记,看看 Request Message 的变化。

public class Data
{
[System.Xml.Serialization.XmlAttribute]
public int I = 1234;

[System.Xml.Serialization.XmlElement]
public string X = "Hello, World!";
}

Request Message

<MessageLogTraceRecord>
<s:Envelope xmlns:s="http://.../">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://...">http://tempuri.org/IContract/Test</Action>
</s:Header>
<s:Body xmlns:xsi="http://..." xmlns:xsd="http://...">
<Test xmlns="http://tempuri.org/">
<d I="123456">
<X>China</X>
</d>
</Test>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>

需要注意一个区别,我们 DataContractSerializer 和 DataContractAttribute 的时候,只有标记了 DataMemberAttribute 的成员被序列化,而 XmlSerializer 是序列化全部 "可视" 成员。

有关 WCF 序列化的更多细节,以后再写相关的研究专题。本文只是作为初次学习的一个简要记录。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: