使用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]
public class SomeError
{
[DataMember]
public string Content;
}
//服务接口
//利用System.ServiceModel.FaultContract特性为操作添加一个出错契约
//这将告诉客户端,服务有可能返回一个由SomeError数据契约定义的格式的错误消息,而不是返回预期的结果
[OperationContract(Name="Faulty")]
[FaultContract(typeof(SomeError))]
decimal DivideByZero(decimal input);
//服务类
public class DerivativesCalculator : IServiceViewOfService
{
[...]
public decimal DivideByZero(decimal input)
{
try
{
decimal denominator = 0;
return input / denominator;
}
catch(Exception exception)
{
SomeError error = new SomeError();
error.Content = exception.Message;
//SomeError的实例通过FaultException<T>泛型传递给调用者
throw new FaultException<SomeError>(error);
}
}
}
//客户端代码
//手动添加接口方法和出错契约
[ServiceContract(Name="DerivativesCalculator")]
[KnownType(typeof(DerivedData))]
public interface IClientViewOfService
{
[...]
[OperationContract(Name="Faulty")]
[FaultContract(typeof(SomeError))]
decimal DivideByZero(decimal input);
}
public class Program
{
public static void 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);
}
[...]
}
}
}