• WCF 之 数据契约


      前面几篇讲的都只能传递string类型的简单参数,数据契约就是用来解决如传递一个带有多个属性的Class类型的对象的。

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

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

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

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

      工程结构如下:

       

    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代码:

    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(); } }
    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:指服务端实现服务契约类的实例模式,有三种类型,分别为:

      [1] PerCall-每次服务操作调用创建一次,调用完后进行销毁;

      [2] PerSession-同一个会话期间创建一次,客户端代理第一次操作(IsInitiating = true)调用创建,调用代理的Close方法销毁或者调用IsTerminating服务操作销毁;

      [3] Single-服务只会创建一次,服务开始时创建,服务完成时销毁。

      [1] PerCall下的会话模式
      单调服务的一个最重要优势在于它能够节省资源,支持系统的可伸缩性。由于服务实例的生命周期只存在于一次调用期间,特别对于那些持有昂贵资源的服务实例而言,这种方式可以有效地提高系统性能。而且,销毁服务实例时,WCF不会断开与客户端(通过客户端的代理)的连接,这比创建实例与连接所消耗的资源要少得多。
      [2] PerSession下的会话模式
      PerSession从字面上就可以理解为,对于每一个客户端代理或者说是会话创建一个实例,代理第一次调用服务契约操作创建实例,当代理调用Close方法或者Terminating方法([OperationContract(IsTerminating = true)])结束会话并释放服务实例,同时服务实例可以被垃圾回收。
      [3] Single下的会话模式
      Single模式下的服务只创建一次,而且是在服务打开的时候,这个模式比较好理解。

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

      最后看客户端代码:

    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;
                }
            }
        }
    }
  • 相关阅读:
    iris数据集
    codevs 1262 不要把球传我 2012年CCC加拿大高中生信息学奥赛
    codevs 1742 爬楼梯(水题日常)
    codevs 2277 爱吃皮蛋的小明(水题日常)
    洛谷 P3386 【模板】二分图匹配
    vijos 1190 繁忙的都市
    codevs 1487 大批整数排序(水题日常)
    洛谷 P2820 局域网
    codevs 1683 车厢重组(水题日常)
    codevs 1228 苹果树
  • 原文地址:https://www.cnblogs.com/xinaixia/p/5779832.html
Copyright © 2020-2023  润新知