注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

天涯倦客的博客

祝福你朋友永远快乐!

 
 
 

日志

 
 

我的WCF之旅(4):WCF中的序列化[上篇1]  

2011-03-24 17:02:45|  分类: WCF |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

SOA 和Message

Windows Communication Foundation (WCF) 是基于面向服务架构(Service Orientation Architecture——SOA)的一种理想的分布式技术(Distributed Technology), 相信在今后在建立基于SOA企业级别的解决方案和进行系统集成方面将会大有作为。一个基于SOA结构的互联系统(Connected System)通常由若干相互独立的子系统(Sub-System)组成,这些子系统可能一个独立的Application,也可能是由若干Application相互集成共同完成一组相关的任务的小系统。这些子系统以一种有效的方式组合、集成为我们听过一种具有综合功能的解决方案。

在一个基于SOA的分布式系统中,各个子系统相互独立又相互关联。说它们的相互独立是因为他们各个都是一个个自治的系统(Autonomous System),可以实行各自的版本策略和部署策略,而这种版本的部署上的变动通常不应该引起系统中其他部分的变动。说它们又彼此关联,则是因为一个子系统所需要的功能往往由其他某个子系统来实现,我们把实现功能的一方称为Service 的提供者(Provider),把使用Service的一方称为客户(Client)。Client依赖于Service的调用,而不失依赖于Service的实现,只要Service的调用方式没有发生改变,Service的实现变动对于Service的使用者来说是完全透明的。在WCF中,我们把Service的调用相关的提取出来即为我们经常说的Contract,Service的提供者和Client之间共享的是Service Contract——而不传统OO概念下的Type。把相对稳定的Service Contract和经常变动的Service Implementation相互分布早就了我们互联系统的松耦合性(Loosely Couple)。

前面我们简单介绍了SOA系统的基本特征——子系统之间的松耦合性(Loosely Couple);各个子系统的自治性(Autonomous);共享Contract。此外SOA还有其他的一些特征,最重要的一个特征就它是一个基于Message(Message-Based)的系统。子系统之间的相互交互由Message来实现。Client向Service的提供者发送一个Soap Message来访为他所需要的Service,Service的提供者监听到来自Client的请求,创建相应的Service对象,执行相关的操作,把执行的结果(Result)以Message 的形式发回给对应的Client。所以我们可以说子系统之间的相互交互本质上是一种消息的交互过程(Message Exchange)。不同的交互方式对应不同的Message Exchange Pattern——MEP。

理解了SO的基本原理,我们来看看WCF,从WCF的全称来分析——Windows Communication Foundation,顾名思义,他就是解决分布式互联系统中各相互独立的子系统如何交互的问题,换句话说,它实际上就是提供 一个基础构架(Infrastructure)实现Application的通信问题。我们前边已经提到,各个子系统之间是通过XML Message进行交互的,所以我们可以 把WCF看成是一个完全处理XML Message的构架,WCF的所有的功能都是围绕着Message来展开的——如何把一个Service的调用转或称一个Message Exchange(Service Contract);如何实现一般的.NET对象和能够容纳于XML Message中的XML Infoset之间的转化(Serialization和Deserialization);如何实现承载数据的XML Infoset和能够用于网络传递的字节流(Byte Stream)之间的相互转化(Encoding和Deconding);如何保证置于Message中数据的一致性和防止被恶意用户窃取以及验证调用Service和通过Service的合法性(Security:Confidentiality,Integrity,Authentication——CIA);如何保证Message被可靠地被传达到所需的地方(Reliable Messaging);以及如何把若干次Service调用——本质上是若干次Message Exchange纳入到一个单独的Conversation(Session Support 和Transaction Support……

在分布式系统中,一个Application与另一个Application之间进行交互,必然需要携带数据。前面我们说了,系统交互完全是应Message的方式进行的,Message是XML,当然置于Message中的数据也应该是XML(XML Infoset)。如何处理这些交互的数据,我们可能首先想到的就是直接处理XML,我们可以在XML级别通过相关的XML技术——XSD,XPath,XSLT来操作数据。但是要使我们处理后的XML需要和要求的完全一致,这样的工作无疑是非常枯燥乏味而且费时费力的。而我们最擅长的就是使用.NET对象来封装我们的数据。如何使我们创造的对象能够有效地转化成结构化的XML Infoset,就是今天我们要讲的内容——Serialization。

Serialization V.S. Encoding

Serialization可以看成是把包含相同内容的数据从一种结构 (.NET Object) 转换成另一种结构 (XML) 。要实现在两种不同结构之间的转化,这两种结构之间必须存在一种Mapping。Serialization的是实现由序列化器(Serializer)来负责。而Serializer则利用某种算法(Arithmetic)来提供这种Mapping。我们知道对于一个Managed Type的结构信息——比如它的所有成员的列表,每个成员的Type、访问限制,以及定在每个成员上的属性,作为原数据被存贮在Assembly的原数据表中,这些原数据可以通过反射的机制获得。而XML的结构一般利用XSD来定义。所以 在WCF中的Serialization可以看成是Serializer通过反射的机制分析对象所对应的Type的原数据,从而提供一种算法实现Managed Type的XSD的转化。

很多刚刚接触WCF的人往往不能很好地区分Serialization和Encoding。我们的.NET Object通过Serialization转化成XML Infoset。但是要使我们的数据能够通过网络协议在网络上传递,必须把生成的XML Infoset转化成字节流(Byte Stream)。所以Encoding关注的是XML Infoset到字节流(Byte Stream)这一段转化的过程。在WCF中,有3中不同的方式可供选择:Binary;Text和MTOM(Message Transmit Optimized Mechanism)。Binary具有最好的Performance,Text具有最好的互操作性,MTOM则有利于大量数据的传送。

我们可以这样来理解Serialization和Encoding,Sterilization是基于Service Contract的——而实际上它也是定义在Service Contract中,是放在我们的Code中;而Encoding一般由Binding提供,它是和Service无关的,我们一般在Configuration中根据实际的需要选择我们合适的Encoding。WCF把Serialization和Encoding相互分离是有好处的,Serialization手部署环境的影响相对不大,具有相对的通用性,而Encoding则关系到访问Service的性能以及互操作性等方面,和部署环境紧密相关。比如对于一个在一个Intranet内部使用的系统,往往处于提高Performance考虑,我们一般是使用TCP Transport结合Binary,可能在某一天出现的来自于Internet的潜在的调用,我们不得不改用Http作为Transport,并使用Text Encoding。由于Encoding是可配置的,所以在这种情况下,我们只需要改变Configuration文件就可以了。

DataContractSerializer

Serialization 是通过Serializer来完成的,在WCF中,我们有3种不同的Serializer——DataContractSerializer(定义在System.RunTime.Serializtion namespace中)、XMLSerializer(定义在System.XML.Serialization namespace)和NetDataContractSerializer (定义在System.XML.Serialization namespace) 。他们不同的方式实现.NET Object的Serialization。由于DataContractSerializer和NetDataContractSerializer基本上没有太大的区别,我们只讨论DataContractSerializer和XMLSerializer。其中DataContractSerializer为WCF默认的Serializer,如果没有显式定采用另外一种Serializer,WCF会创建一个DataContractSerializer 序列化NET Object。首先我们来讨论DataContractSerializer采用怎样的一种Mapping方式来把.NET Object转化成XML。我们照例用实验来说明问题。

我们创建两个类DataContractProduct和DataContractOrder用于表示产品和订单两个实体,读者完全可以命知道描述的内容,这里不作特别的介绍。  

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace Artech.WCFSerialization
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客
{
    [DataContract]
    
public class DataContractProduct
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客    
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
#region Private Fields
        
private Guid _productID;
        
private string _productName;
        
private string _producingArea;
        
private double _unitPrice;
        
#endregion


我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
#region Constructors
        
public DataContractProduct()
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            Console.WriteLine(
"The constructor of DataContractProduct has been invocated!");
        }


        
public DataContractProduct(Guid id, string name, string producingArea, double price)
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            
this._productID = id;
            
this._productName = name;
            
this._producingArea = producingArea;
            
this._unitPrice = price;
        }


        
#endregion


我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
#region Properties
        [DataMember]
        
public Guid ProductID
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _productID; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _productID = value; }
        }


        [DataMember]
        
public string ProductName
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _productName; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _productName = value; }
        }


        [DataMember]
        
private string ProducingArea
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _producingArea; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _producingArea = value; }
        }


        [DataMember]
        
public double UnitPrice
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _unitPrice; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _unitPrice = value; }
        }


        
#endregion


    }

}

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace Artech.WCFSerialization
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客
{
    [DataContract]
    [KnownType(
typeof(DataContractOrder))]
    
public class DataContractOrder
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客    
{
        
private Guid _orderID;
        
private DateTime _orderDate;
        
private DataContractProduct _product;
        
private int _quantity;

我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
#region Constructors
        
public DataContractOrder()
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            
this._orderID = new Guid();
            
this._orderDate = DateTime.MinValue;
            
this._quantity = int.MinValue;

            Console.WriteLine(
"The constructor of DataContractOrder has been invocated!");
        }


        
public DataContractOrder(Guid id, DateTime date, DataContractProduct product, int quantity)
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            
this._orderID = id;
            
this._orderDate = date;
            
this._product = product;
            
this._quantity = quantity;
        }

        
#endregion


我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
#region Properties
        [DataMember]
        
public Guid OrderID
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _orderID; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _orderID = value; }
        }

        [DataMember]
        
public DateTime OrderDate
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _orderDate; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _orderDate = value; }
        }

        [DataMember]
        
public DataContractProduct Product
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _product; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _product = value; }
        }

        [DataMember]
        
public int Quantity
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
get return _quantity; }
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
set { _quantity = value; }
        }

        
#endregion


        
public override string ToString()
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            
return string.Format("ID: {0}\nDate:{1}\nProduct:\n\tID:{2}\n\tName:{3}\n\tProducing Area:{4}\n\tPrice:{5}\nQuantity:{6}",
                
this._orderID, this._orderDate, this._product.ProductID, this._product.ProductName, this._product.ProducingArea, this._product.UnitPrice, this._quantity);
        }

    }

}

使用DataContractSerializer序列化.NET Object。相关的Type必须运用System.Runtime.Serialization. DataContractAttribute, 需要序列化的成员必须运用System.Runtime.Serialization. DataMemberAttribute。为了使我们能够了解DataContract默认的Mapping机制,我们暂时不在DataContractAttribute和DataMemberAttribute设置任何参数。下面我们 来编写具体的Serialization的代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace Artech.WCFSerialization
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客
{
    
class Program
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客    
{
        
static string _basePath = @"E:\Projects\Artech.WCFSerialization\Artech.WCFSerialization\";

        
static void Main(string[] args)
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            SerializeViaDataContractSerializer();
        }

        
static void SerializeViaDataContractSerializer()
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客        
{
            DataContractProduct product 
= new DataContractProduct(Guid.NewGuid(), "Dell PC""Xiamen FuJian"4500);
            DataContractOrder order 
= new DataContractOrder(Guid.NewGuid(), DateTime.Today, product, 300);
            
string fileName = _basePath + "Order.DataContractSerializer.xml";
            
using (FileStream fs = new FileStream(fileName, FileMode.Create))
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客            
{
                DataContractSerializer serializer 
= new DataContractSerializer(typeof(DataContractOrder));
                
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs))
我的WCF之旅(4):WCF中的序列化[上篇1] - 海里的贝壳 - apple的博客                
{
                    serializer.WriteObject(writer, order);
                }

            }

            Process.Start(fileName);
        }

    }

}

代码很简单,这里不作特别介绍。我们现在只关心生成XML是怎样的结构: 

这里我们总结出以下的Mapping关系:

1.        Root Element为对象的Type Name——DataContractOrder

2.        Type的Namespace会被加到XML根节点的Namespace中http://schemas.datacontract.org/2004/07/Artech.WCFSerialization

3.        对象的所有成员以XML Element的形式而不是以XML Attribute的形式输出。

4.        所以对象在XML的输出顺序是按照字母排序。

5.        所有成员的Elelement 名称为成员名称。

6.        不论成员设置怎样的作用域(public,protected,internal,甚至市Private),

所有运用了DataMemberAttribute的成员均被序列化到XML中——private string ProducingArea。

7.        Type和成员必须运用DataContractAttribute和DataMemberAttribute才能被序列化。

 

上面这些都是默认的Mapping关系,在通常情况下我们用默认的这种Mapping往往不能满足我们的需求,为了把.NET序列化成我们需要的XML 结构(比如我们的XmL必须于我们预先定义的XSD一致),我们可以在这两个Attribute(DataContractAttribute和DataMemberAttribute)制定相关的参数来实现。具体做法如下。

 

1.        Root Element可以通过DataContractAttribute中的Name参数定义。

2.        Namespace可以通过DataContractAttribute中的NameSpace参数定义。

3.        对象的成员只能以XML Element的形式被序列化。

4.        对象成员对应的XML Element在XML出现的位置可以通过DataMemberAttribute的Order参数来定义。

5.        对象成员对应的Element的名称可以通过DataMemberAttribute中的Name定义。

6.        如果不希望某个成员输出到XML中,可以去掉成员对应的DataMemberAttribute Attribute。

此外DataMemberAttribute还有连个额外的参数:

1.         IsRequired:制定该成员为必须的,如果通过工具生成XSD的话,对应的Element的minOccur=“1”

2.        EmitDefaultValue:制定是否输入没有赋值的成员(值为默认值)是否出现在XML中。

基于上面这些,现在我们修改我们的DataContractOrder和DataContractProduct。

 

  评论这张
 
阅读(476)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017