WCF开发之数据契约

前面几篇讲的都只能传递string类型的简单参数,如果要传递一个带有多个属性的Class类型的对象要怎么办那???数据契约就是用来解决这个问题的!

WCF推荐使用数据契约的方式实现数据的序列化。这部分的内容很好理解但是很重要,先看[DataContract]和[DataMember]这两个就是数据契约的标记,他们在System.Runtime.Serialization命名空间下。

 

[DataContract]:它用来实现Schema与CLR类型之间的转换。总是要提供命名空间,Web Service的规范要求使用“Schemas”前缀来表示服务目标的命名空间。

[DataMember]:明确成员是否参与序列化;应用与域或属性;可以指定排列顺序,缺省按照字母表顺序排列;能够显示提供Name, IsRequired。

注:对于复杂类型使用DataContractSerializer是一种较好的方法。下面用一个Demo来更好的说明:

工程结构如下:

 WCF开发之数据契约

ContentTypes.LinkItem代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Runtime.Serialization;

namespace  ContentTypes
{
    [DataContract(Name 
=   " LinkItem_Contract " , Namespace  =   " http://www.cnblogs.com/Charlesliu " )]
    
public   class  LinkItem
    {
        
private   long  m_id;
        
private   string  m_title;
        
private   string  m_description;
        
private  DateTime m_dateStart;
        
private  DateTime m_dateEnd;
        
private   string  m_url;

        [DataMember(Name 
=   " Id_Contract " , IsRequired  =   false , Order  =   0 )]
        
public   long  Id
        {
            
get  {  return  m_id; }
            
set  { m_id  =  value; }
        }

        [DataMember(Name 
=   " Title_Contract " , IsRequired  =   true , Order  =   1 )]
        
public   string  Title
        {
            
get  {  return  m_title; }
            
set  { m_title  =  value; }
        }

        [DataMember(Name 
=   " Description_Contract " , IsRequired  =   true , Order  =   2 )]
        
public   string  Description
        {
            
get  {  return  m_description; }
            
set  { m_description  =  value; }
        }

        [DataMember(Name 
=   " DateStart_Contract " , IsRequired  =   true , Order  =   3 )]
        
public  DateTime DateStart
        {
            
get  {  return  m_dateStart; }
            
set  { m_dateStart  =  value; }
        }

        [DataMember(Name 
=   " DateEnd_Contract " , IsRequired  =   false , Order  =   4 )]
        
public  DateTime DateEnd
        {
            
get  {  return  m_dateEnd; }
            
set  { m_dateEnd  =  value; }
        }

        [DataMember(Name 
=   " Url_Contract " , IsRequired  =   false , Order  =   5 )]
        
public   string  Url
        {
            
get  {  return  m_url; }
            
set  { m_url  =  value; }
        }
    }
}

对上面的代码说明一下,[DataContract]的Name也是改变Client看到的名称,namespace和以前一样。[DataMember]推荐使用在属性上,Name的作用也是改变客户端看到的数据类的名字; IsRequired的作用是,当服务段添加了一个新的DataMember但是客户端没有更新服务的引用时,如果IsRequired=true,就会报错,原应是true代表这个DataMember一定要序列化才能正常通信,由于此时客户端不知道这个新DataMember的存在没有序列化它,所以报错了;IsRequired=false,可以正常使用。Order可以指定排列顺序,缺省按照字母表顺序排列。通过这两个标记定义的对象就可以在服务端和客户端之间传递了,WCF会自动实现数据的序列化等操作。

再看一下Service代码:

IGigManagerService代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  ContentTypes;

namespace  WcfServiceLibraryDemo
{
    [ServiceContract(Name 
=   " GigManagerServiceContract " , Namespace  =   " http://www.cnblogs.com/Charlesliu " , SessionMode  =  SessionMode.Required)]
    
public   interface  IGigManagerService
    {
        [OperationContract(Name 
=   " SaveGig " , Action  =   " http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/SaveGig " , ReplyAction  =   " http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/SaveGigResponse " )]
        
void  SaveGig(LinkItem item);

        [OperationContract(Name 
=   " GetGig " , Action  =   " http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGig " , ReplyAction  =   " http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGigResponse " )]
        LinkItem GetGig();
    }
}
GigManagerService 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;
using  ContentTypes;

namespace  WcfServiceLibraryDemo
{
    [ServiceBehavior(InstanceContextMode 
=  InstanceContextMode.PerSession)]
    
public   class  GigManagerService : IGigManagerService
    {

        
private  LinkItem m_linkItem;

        
#region  IGigManager Members

        
public   void  SaveGig(LinkItem item)
        {
            m_linkItem 
=  item;
        }

        
public  LinkItem GetGig()
        {
            
return  m_linkItem;
        }

        
#endregion
    }
}

 

上面的代码很简单,不多说了。这里引出了一个关键的东西会话设置,[ServiceContract(SessionMode=SessionMode.Required)]应用在服务的接口上。
SessionMode是客户端代理与服务器之间的会话模式,同样也有三种类型:Allowed-允许会话、NotAllowed-不允许会话、Required-要求会话(需要有支持会话的Binding支持,WsHttpBinding、NetTcpBinding等)。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]应用在服务的类上。
InstanceContextMode为服务端实现服务契约类的实例模式,有三种类型,分别为:PerCall-每次服务操作调用创建一次,调用完后进行销毁;PerSession-同一个会话期间创建一次,客户端代理第一次操作(IsInitiating = true)调用创建,调用代理的Close方法销毁或者调用IsTerminating服务操作销毁;Single-服务只会创建一次,服务开始时创建,服务完成时销毁。
PerCall下的会话模式
单调服务的一个最重要优势在于它能够节省资源,支持系统的可伸缩性。由于服务实例的生命周期只存在于一次调用期间,特别对于那些持有昂贵资源的服务实例而言,这种方式可以有效地提高系统性能。而且,销毁服务实例时,WCF不会断开与客户端(通过客户端的代理)的连接,这比创建实例与连接所消耗的资源要少得多。
PerSession下的会话模式
PerSession从字面上就可以理解为,对于每一个客户端代理或者说是会话创建一个实例,代理第一次调用服务契约操作创建实例,当代理调用Close方法或者Terminating方法([OperationContract(IsTerminating = true)])结束会话并释放服务实例,同时服务实例可以被垃圾回收。
Single下的会话模式
Single模式下的服务只创建一次,而且是在服务打开的时候,这个模式比较好理解。

如果把上面的代码中的PreSession改为PreCall,那么GetGig方法就会失效了,m_linkItem保存不了Save提交的数据。

最后看客户端代码:

Form1 代码
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Windows.Forms;
using  WinTest.MyServiceReference;

namespace  WinTest
{
    
public   partial   class  Form1 : Form
    {
        MyServiceReference.GigManagerServiceContractClient m_proxy 
=   new  WinTest.MyServiceReference.GigManagerServiceContractClient();

        
public  Form1()
        {
            InitializeComponent();
        }

        
private   void  cmdSave_Click( object  sender, EventArgs e)
        {
            LinkItem_Contract item 
=   new  LinkItem_Contract();

            item.Id_Contract 
=   int .Parse( this .txtId.Text);
            item.Title_Contract 
=   this .txtTitle.Text;
            item.Description_Contract 
=   this .txtDescription.Text;
            item.DateStart_Contract 
=   this .dtpStart.Value;
            item.DateEnd_Contract 
=   this .dtpEnd.Value;
            item.Url_Contract 
=   this .txtUrl.Text;

            m_proxy.SaveGig(item);
        }

        
private   void  cmdGet_Click( object  sender, EventArgs e)
        {
            LinkItem_Contract item 
=  m_proxy.GetGig();
            
if  (item  !=   null )
            {
                
this .txtId.Text  =  item.Id_Contract.ToString();
                
this .txtTitle.Text  =  item.Title_Contract;
                
this .txtDescription.Text  =  item.Description_Contract;

                
if  (item.DateStart_Contract  !=  DateTime.MinValue)
                    
this .dtpStart.Value  =  item.DateStart_Contract;
                
if  (item.DateEnd_Contract  !=  DateTime.MinValue)
                    
this .dtpEnd.Value  =  item.DateEnd_Contract;

                
this .txtUrl.Text  =  item.Url_Contract;
            }
        }
    }
}

Client端的代码没什么可讲的很好理解。(完)

你可能感兴趣的:(WCF)