首页 > 代码库 > WCF揭秘学习笔记(3):使用DataContractSerializer

WCF揭秘学习笔记(3):使用DataContractSerializer

使用DataContractSerializer

  终结点(包括地址、绑定、契约)可通过代码以编程方式添加到服务中。如:

using(ServiceHost host =new ServiceHost(typeof(DerivativesCalculator),
new Uri[] { new Uri("http://localhost:8000/Derivatives") }))
{
host.AddServiceEndpoint(
typeof(IServiceViewOfService), new BasicHttpBinding(), "Calculator");
host.Open();

Console.WriteLine(
"The service is available.");

[......]
}

  类似地,客户端亦可直接通过编程方式包含服务终结点信息,而无需引用应用程序配置的终结点信息:

string address ="http://localhost:8000/Derivatives/Calculator";
ChannelFactory
<IClientViewOfService> factory =new ChannelFactory(IClientViewOfService>(
new BasicHttpBinding(),
new EndpointAddress(new Uri(address)));
IClientViewOfService proxy
= factory.CreateChannel();

  DataContractSerializer能自动序列化任何实现了System.Runtime.Serialization.ISerializable接口的.NET类型。

  通过实现System.Runtime.Serialization.IExtensibleDataObject接口,类会另外分配一些空 间,这样System.Runtime.Serialization.DataContractSerializer可以用来存储、访问数据契约其他版本 可能包含的其他成员的值。这样,当同一个数据契约的更高级版本包含服务版本所没有的成员 时,System.Runtime.Serialization.DataContract-Serializer就能将这些成员的值传给服务。

  当需要使用参数类型的子类型的实例作为输入来访问服务时,就必须添加 System.ServiceModel.ServiceKnownType特性,这意味着我们应该避免使用继承关系作为数据契约升级版本的方法。如果定 义的是父类型参数,但接收到的却是子类型,除非修改代码为预测到的子类型添加System.ServiceModel.ServiceKnownType 特性,否则子类型的序列化将会失败。

  通过使用System.Runtime.Serialization.DataContract和 System.Runtime.Serialization.DataMember特性,可以非常简单地让WCF的 System.Runtime.Serializa-tion.DataContractSerializer将开发者定制的数据类型序列化。

  实现System.Runtime.Serialization.IExtensibleDataObject接口通常是一个很好的做法,因为这样同一数据契约的不同版本可以相互独立地改进,而它们又能一起使用。

  WCF对System.Runtime.Serialization.DataContractSerializer的调用被隐藏起来了,以下代码显示调用了DataContractSerializer将数据序列化成XML:

MemoryStream stream =new MemoryStream();
DataContractSerializer serializer
=new DataContractSerializer(typeof(ClientViewOfData));
serializer.WriteObject(stream, calculation);
Console.WriteLine(UnicodeEncoding.UTF8.GetChars(stream.GetBuffer());

异常处理

  当服务发生异常时,数据契约也能帮助通知客户端。如下所示:

//服务器端代码
//数据契约
[DataContract]
publicclass SomeError
{
[DataMember]
publicstring Content;
}

//服务接口
//利用System.ServiceModel.FaultContract特性为操作添加一个出错契约
//这将告诉客户端,服务有可能返回一个由SomeError数据契约定义的格式的错误消息,而不是返回预期的结果
[OperationContract(Name="Faulty")]
[FaultContract(
typeof(SomeError))]
decimal DivideByZero(decimal input);

//服务类
publicclass DerivativesCalculator : IServiceViewOfService
{
[...]
publicdecimal DivideByZero(decimal input)
{
try
{
decimal denominator =0;
return input / denominator;
}
catch(Exception exception)
{
SomeError error
=new SomeError();
error.Content
= exception.Message;
//SomeError的实例通过FaultException<T>泛型传递给调用者
thrownew FaultException<SomeError>(error);
}
}
}

//客户端代码
//手动添加接口方法和出错契约
[ServiceContract(Name="DerivativesCalculator")]
[KnownType(
typeof(DerivedData))]
publicinterface IClientViewOfService
{
[...]
[OperationContract(Name
="Faulty")]
[FaultContract(
typeof(SomeError))]
decimal DivideByZero(decimal input);
}

publicclass Program
{
publicstaticvoid Main(string[] args)
{
using(ServiceHost host =new ServiceHost(typeof(DerivativesCalculator),
new Uri[]{ new Uri("http://localhost:8000/Derivatives") }))
{
host.AddServiceEndpoint(
typeof(IServiceViewOfService),
new BasicHttpBinding(), "Calculator");
host.Open();

Console.WriteLine(
"The service is available.");
string address ="http://localhost:8000/Derivatives/Calculator";

ChannelFactory
<IClientViewOfService> factory =new ChannelFactory<IClientViewOfService>(
new BasicHttpBinding(), new EndpointAddress(new Uri(address)));
IClientViewOfService proxy
= factory.CreateChannel();

[...]

try
{
Decimal quotient
= proxy.DivideByZero(9);
}
catch(FaultException<SomeError> error)
{
Console.WriteLine(
"Error: {0}", error.Detail.Content);
}

[...]
}
}
}

  WCF也提供了给客户端返回任何在服务中发生的未预测到的异常的完整信息的功能,这通常在调度一个服务的过程中很有用。这个功能可以通过使用 System.ServiceModel-.DescriptionServiceDebugBehavior行为的 IncludeExceptionDetailInFaults属性来打开。该属性可在配置文件中设置,这将导致任何未被处理的异常都传输给客户端。但在 最后的产中版本里打开此属性将会是不安全的,因为将异常的所有信息都传递给客户端可能会暴露一些对服务会造成危害的信息。