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

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 序列化的更多细节,以后再写相关的研究专题。本文只是作为初次学习的一个简要记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值