WCF分布式开发步步为赢(7):WCF数据契约与序列化

  本节继续学习WCF分布式开发步步为赢(7):WCF数据契约与序列化.数据契约是WCF应用程序开发中一个重要的概念,毫无疑问实现客户端与服务端数据契约的传递中序列化是非常重要的步骤。那么序列化是什么?为什么会有序列化机制?或者说它是为了解决什么问题?作用是什么?现有的.NET 序列化机制和WCF序列化机制有什么不同?我们在本节文章里都会详细介绍。

  下面我们正式进入今天的学习阶段,首先来介绍一下数据契约的概念:

  【0】数据契约(DataContract):

   在WCF服务编程中我们知道,服务契约定义了远程访问对象和可供调用的服务操作方法,数据契约则是定义服务端和客户端之间要传送的自定义数据类型。在WCF项目中,声明一个类型为DataContract,那么该类型就可以被序列化在服务端和客户端之间传送。类只有声明为DataContract,该类型的对象才可以被传送,且只有类的属性会被传送,需要在属性生命前加DataMember声明,这样该属性就可以被序列化传送。默认情况属性是不可传递的。类的方法不会被传送。WCF对定义的数据契约的类型可以进行更加细节的控制,可以把一个成员属性排除在序列化范围以外,客户端程序不会获得被排除在外的成员属性的任何信息,包括定义和数据。  代码如下:

 [DataContract]//数据契约属性声明
    class MyDataContract
    {
        [DataMember(Name 
= "MyName")]//数据成员标记,支持别名定义
        public string Name
        {
            
get;
            
set;
        }
        [DataMember(Name 
= "MyEmail")]//数据成员标记,支持别名定义
        public string Email
        {
            
get;
            
set;
        }
        [DataMember]
//数据成员标记
        public string Mobile
        {
            
get;
            
set;
        }
        
//没有[DataMember]声明,不会被序列化
        public string Address
        {
            
get;
            
set;
        }
    }
}

  【1】序列化基本概念:

  知道数据契约的一些概念和特性之后,下面来介绍一下序列化的概念。

  【1.1】为什么序列化:我们这里先来介绍一下为什么需要序列化。当然这个不是必须的。只是针对特定的开发平台的数据或者信息类型而言,当一个系统或者说平台需要和别的异构的系统或者平台交互的时候,两个系统需要一个特定的公开的可以公用的行业标准来支持这个数据信息的交互。这里目前来说支持这个数据交互传递的语言载体就是XML.

  同样WCF作为面向服务的编程框架,它的目标或者特性之一就是实现服务的跨语言、平台,与不同的服务进行信息数据的交互,而不限制客户端的系统或者开发语言。要实现这个目标,WCF服务首先就是要面对信息的传递与共享问题。我们知道WCF服务和客户端可以传递如Int、String等.NET数据类型。但是如何实现用户自定义复杂类型的跨服务边界的传递,这是一个关键问题。数据契约可以发布为服务的元数据,允许客户端转化为本地语言表示。解决的办法就是封送(Marshaling),将对象封送到其它平台。基于WCF的客户端和服务端参数传递的过程如下图:

  主要步骤:客户端序列化参数为XML信息集--传递->服务端反序列化为本地类型--执行结果->序列化结果为XML信息集--传递->客户端序反序列化返回信息为本地类型。

  在WCF分布式开发必备知识(2):.NET Remoting一节中也介绍了.NET Remoting的通信过程 ,两者也有流程也有部分相似之处。对象封送的概念其实.NET Remoting早有涉及,远程对象(RemoteOject),也就是我们远程要访问的对象.首先定义一个Class,继承MarshalByRefObject,可以使用在remoting应用中,支持对象的跨域边界访问。看过.NET Remoting这节文章应该还有点印象,不同之处是WCF的对象封送是为跨越服务边界,.NET Remoting的封送是为了跨越跨域边界。相关的概念请查阅WCF分布式开发必备知识(2):.NET Remoting或者MSDN,都能找到详细的介绍,这里不在详述。

  【1.2】什么是序列化:

  序列化是指将对象实例的状态存储存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。

  用户自定义类型要想在WCF服务端和客户端传递就必须声明为DataContract。这样就能实现用户自定义类型的序列化。序列化的目的就是把一种私有的或者某种平台下使用的数据类型转化为标准的可以公开交互的数据信息样式。这个过程就叫序列化。这个也是序列化的作用或者目的之所在。序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。序列化的目的:

  1、以某种存储形式使自定义对象持久化;

  2、将对象从一个地方传递到另一个地方。 序列化就是把本地消息或者数据的类型进行封送,转换为标准的可以跨平台、语言的信息集,为别的系统或者服务所用。

  【2】.NET 序列化机制:

  .NET的序列化。.NET是通过反射机制自动实现对象的序列化与反序列化。首先.NET能够捕获对象每个字段的值,然后进行序列化,反序列化时,.NET创建一个对应类型的新的对象,读取持久化的值,然后设置字段的值。.NET对象状态的序列化到Stream中。.NET Framework 提供三种序列化技术:

  1.BinaryFormatter二进制序列化保持类型保真度,这对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等等。远程处理使用序列化“通过值”在计算机或应用程序域之间传递对象。

     2.调用System.Runtime.Serialization.Formatters.Soap空间下的SoapFormatter类进行序列化和反序列化,序列化之后的文件是Soap格式的文件(简单对象访问协议(Simple Object Access Protocol,SOAP),WCF分布式开发必备知识(4):Web Service也进行了介绍,SOAP是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。 SOAP使用基于XML的数据结构和超文本传输协议(HTTP)的组合定义了一个标准的方法来使用InterNET上各种不同操作环境中的分布式对象。可以实现跨平台数据的交互。

  3.XML 序列化需要引用System.Xml.Serialization,使用XmlSerialize类进行序列化和反序列化操作。它仅序列化公共属性和字段,且不保持类型保真度。基于XML 标准,支持跨平台数据交互。

    BinaryFormatter二进制序列化的优点:1. 所有的类成员(包括只读的)都可以被序列化;2. 性能高。

    SoapFormatter、XmlSerialize的优点:1. 跨平台,互操作性好;2. 不依赖二进制;3. 可读性强。

    当然.NET原有的序列化器也有自己的局限性,因为他们除了要序列化对象的状态信息外,还要将程序集的信息和版本信息持久化到流中,这样才能保证对象被反序列化为正确的对象类型副本。这要求客户端必须拥有使用.NET程序集。不能满足跨平台的WCF数据交互的需求。

  【3】WCF序列化机制:

  由于.NET格式化器固有的缺陷,WCF不得不提供自己的格式化,以满足其面向服务的需求。在WCF程序集里,提供了专门用户序列化和反序列化操作的类:DataContractSerializer,在System.Runtime.Serialization命名空间里。使用Reflector查看其定义信息如下:

public abstract class XmlObjectSerializer
{
    
// Methods
    protected XmlObjectSerializer()
    {
    }
    
internal static void CheckNull(object obj, string name)
    {
        
if (obj == null)
        {
            
throw new ArgumentNullException(name);
        }
    }
    
internal virtual Type GetSerializeType(object graph)
    {
        
if (graph != null)
        {
            
return graph.GetType();
        }
        
return null;
    }
    
private static string GetTypeInfoError(int errorMessage, Type type, Exception
innerException)
    {
        
string str = (type == null? string.Empty : SR.GetString(1new object[]
{ DataContract.GetClrTypeFullName(type) });
        
string str2 = (innerException == null? string.Empty : innerException.Message;
        
return SR.GetString(errorMessage, new object[] { str, str2 });
    }

    
public abstract bool IsStartObject(XmlDictionaryReader reader);
    
public virtual bool IsStartObject(XmlReader reader)
    {
        CheckNull(reader, 
"reader");
        
return this.IsStartObject(XmlDictionaryReader.CreateDictionaryReader(reader));
    }

    
public virtual object ReadObject(XmlDictionaryReader reader)
    {
        
return this.ReadObject(reader, true);
    }
    
public virtual object ReadObject(XmlReader reader)
    {
        CheckNull(reader, 
"reader");
        
return this.ReadObject(XmlDictionaryReader.CreateDictionaryReader(reader));
    }
    
public abstract object ReadObject(XmlDictionaryReader reader, bool
verifyObjectName);
    
public virtual object ReadObject(XmlReader reader, bool verifyObjectName)
    {
        CheckNull(reader, 
"reader");
        
return this.ReadObject(XmlDictionaryReader.CreateDictionaryReader(reader),
verifyObjectName);
    }

    
public abstract void WriteEndObject(XmlDictionaryWriter writer);
    
public virtual void WriteEndObject(XmlWriter writer)
    {
        CheckNull(writer, 
"writer");
        
this.WriteEndObject(XmlDictionaryWriter.CreateDictionaryWriter(writer));
    }
    
public virtual void WriteObject(XmlDictionaryWriter writer, object graph)
    {
        CheckNull(writer, 
"writer");
        
try
        {
            
this.WriteStartObject(writer, graph);
            
this.WriteObjectContent(writer, graph);
            
this.WriteEndObject(writer);
        }
        
catch (XmlException exception)
        {
            
throw new SerializationException(GetTypeInfoError(0this.GetSerializeTy
pe(graph), exception), exception);
        }
        
catch (FormatException exception2)
        {
            
throw new SerializationException(GetTypeInfoError(0this.GetSerializeTy
pe(graph), exception2), exception2);
        }
    }
    
public virtual void WriteObject(XmlWriter writer, object graph)
    {
        CheckNull(writer, 
"writer");
        
this.WriteObject(XmlDictionaryWriter.CreateDictionaryWriter(writer), graph);
    }
    
public abstract void WriteObjectContent(XmlDictionaryWriter writer, object
graph);
    
public virtual void WriteObjectContent(XmlWriter writer, object graph)
    {
        CheckNull(writer, 
"writer");
        
this.WriteObjectContent(XmlDictionaryWriter.CreateDictionaryWriter(writer),
graph);
    }
    
public abstract void WriteStartObject(XmlDictionaryWriter writer, object graph);
    
public virtual void WriteStartObject(XmlWriter writer, object graph)
    {
        CheckNull(writer, 
"writer");
        
this.WriteStartObject(XmlDictionaryWriter.CreateDictionaryWriter(writer),
 graph);
    }
}

  【4】代码实现与分析:

  下面是示例代码的具体实现过程,这里做简要的讲解.

  【4.1】数据契约:

  我们定义了一个数据契约类UserDataContract,包含简单的几个属性Name、Email、Mobile,分别用来存储用户名、电子邮件、手机信息,代码如下:

  [DataContract]//数据契约属性声明
    class UserDataContract
    {
        [DataMember(Name 
= "UserName")]//数据成员标记,支持别名定义
        public string Name
        {
            
get;set;
        }
        [DataMember(Name 
= "UserEmail")]//数据成员标记,支持别名定义
        public string Email
        {
            
get;set;
        }
        [DataMember]
//数据成员标记
        public string Mobile
        {
            
get;set;
        }
        
//没有[DataMember]声明,不会被序列化
        public string Address
        {
            
getset;
        }
    }

NET技术WCF分布式开发步步为赢(7):WCF数据契约与序列化,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。