【学习】WCF的服务契约、复杂类型序列化、消息契约的实现

 第二节

【学习】构建WCF面向服务的应用程序系列课程笔记:(2) 契约设计

的内容比较多,需要时间消化一下。这几天工作也比较忙,节奏慢了哦,罪过罪过。

本文对应第二节演示的DEMO,自己针对服务契约、复杂类型序列化中的DataContract、KnownType(实现多态)、IXmlSerialiable与消息契约动手实践了一下。

 

ServiceContract:

BussinessServices

项目中写了一个IServiceA的服务契约,类ServiceA为IServiceA的实现:

IServiceA:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace BussinessServices
{
    [ServiceContract]
    public interface IServiceA
    {
        [OperationContract]
        string Operation1();

        [OperationContract]
        string Operation2();

        [OperationContract(Name="OperationABC")]
        string Operation3();
    }
}

ServiceA:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BussinessServices
{
    public class ServiceA:IServiceA
    {
        public string Operation1()
        {
            return "IServiceA Operation1 invoked.";
        }

        public string Operation2()
        {
            return "IServiceA Operation2 invoked.";
        }


        public string Operation3()
        {
            return "IServiceA 中定义的方法为Operation3,但客户端调用是用OperationABC.";
        }
    }
}


Host项目运行服务:

App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="BussinessServices.ServiceA">
        <endpoint address="ServiceA" binding="wsHttpBinding" contract="BussinessServices.IServiceA">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost hostA = null;

            try
            {
                hostA = new ServiceHost(typeof(BussinessServices.ServiceA));
                hostA.Open();

                Console.WriteLine();
                Console.WriteLine("Press <Enter> to closed host.");
                Console.ReadLine();
            }
            catch (System.Exception ex)
            {
            	
            }
            finally
            {
                hostA.Close();
            }
        }
    }
}


Client客户端项目:

首先运行Host,并在Client客户端中引用服务引用,生成LocalHost代码,在主窗口中拖放三个按钮:

代码如下:

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;

namespace Client
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (LocalHost.ServiceAClient proxy = new LocalHost.ServiceAClient())
            {
                string s = proxy.Operation1();
                MessageBox.Show(s);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (LocalHost.ServiceAClient proxy = new LocalHost.ServiceAClient())
            {
                string s = proxy.Operation2();
                MessageBox.Show(s);
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            using (LocalHost.ServiceAClient proxy = new LocalHost.ServiceAClient())
            {
                //OperationABC是IService契约的Operation3
                string s = proxy.OperationABC();
                MessageBox.Show(s);
            }
        }
    }
}


到此这个例子结束,运行时选运行Host,开启服务,然后运行Client,分别点击三个按钮,看到效果。

 

DataContract

这个例子的项目结构见下图:

Person.cs:

 

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

namespace ContentTypes
{
    /// <summary>
    /// 人的基类
    /// </summary>
    [DataContract]
    public class Person
    {
        private string m_sn;
        private string m_name;
        private string m_sex;

        /// <summary>
        /// 编号
        /// </summary>
        [DataMember]
        public string SN
        {
            get { return m_sn; }
            set { m_sn = value; }
        }

        /// <summary>
        /// 姓名
        /// </summary>
        [DataMember]
        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        /// <summary>
        /// 性别
        /// </summary>
        [DataMember]
        public string Sex
        {
            get { return m_sex; }
            set { m_sex = value; }
        }
    }
}


服务IPersonManagerService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ContentTypes;

namespace BussinessServices
{
    [ServiceContract]
    public interface IPersonManagerService
    {
        [OperationContract]
        Person GetPerson();

        [OperationContract]
        void SetPerson(Person person);
    }

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class PersonManagerService : IPersonManagerService
    {
        private Person m_person;

        public Person GetPerson()
        {
            return m_person;
        }

        public void SetPerson(Person person)
        {
            m_person = person;
        }
    }
}


Host项目的App.config文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="BussinessServices.PersonManagerService">
        <endpoint address="Person" binding="wsHttpBinding" contract="BussinessServices.IPersonManagerService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>


Host项目的Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(BussinessServices.PersonManagerService)))
            {
                host.Open();
                Console.WriteLine();
                Console.WriteLine("Press <Enter> to closed host.");
                Console.ReadLine();
            }
        }
    }
}


PersonEntry

客户端同样先使用服务引用,VS会自动生成调用服务的代码,我这里的命名是localhost,在主窗体中放了对应Person类的属性的TextBox,两个按钮分别是保存:负责取表单中数据并保存,获取按钮从服务取得数据并加载到表单中。

代码:

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;

namespace PersonEntry
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        localhost.PersonManagerServiceClient proxy = new localhost.PersonManagerServiceClient();

        private void btn_Save_Click(object sender, EventArgs e)
        {
            localhost.Person person = new localhost.Person();
            person.SN = txt_SN.Text;          
            person.Sex = txt_Sex.Text;
            person.Name = txt_Name.Text;

            proxy.SetPerson(person);
            MessageBox.Show("info set sucess.");
        }

        private void btn_GetInfo_Click(object sender, EventArgs e)
        {
            var person = proxy.GetPerson();
            if (person != null)
            {
                txt_SN.Text = person.SN;
                txt_Sex.Text = person.Sex;
                txt_Name.Text = person.Name;
                MessageBox.Show("info get sucess.");
                return;
            }
       
            MessageBox.Show("请先点击【保存】按钮,保存成功后,改变表单数据,再【获取】!");
        }
    }
}


 

运行效果:

KnownTypesDataContracts

实现多态的例子,项目结构见下图:

对多态的实现有四种方式:

  • 在实体类上使用KnownType
  • 在契约上使用ServiceKnownType
  • 在契约的方法中使用ServiceKnownType
  • 通过配置文件实现

在代码中有三种方式,详细看注释,第4种自己没有测试,就没写了。

Person.cs:

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

namespace ContentTypes
{
    /// <summary>
    /// 人的基类
    /// </summary>
    [DataContract]
    //第一种方式,在基类上指定KnownType,标示子类,实现多态
    [KnownType(typeof(Employee))]       
    public class Person
    {
        private string m_sn;
        private string m_name;
        private string m_sex;

        /// <summary>
        /// 编号
        /// </summary>
        [DataMember]
        public string SN
        {
            get { return m_sn; }
            set { m_sn = value; }
        }

        /// <summary>
        /// 姓名
        /// </summary>
        [DataMember]
        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        /// <summary>
        /// 性别
        /// </summary>
        [DataMember]
        public string Sex
        {
            get { return m_sex; }
            set { m_sex = value; }
        }
    }
}


可以在基类上用KnownType指定子类,见代码注释。

Employee.cs:

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

namespace ContentTypes
{
    [DataContract]
    public class Employee : Person
    {
        private string m_id;

        /// <summary>
        /// 成员ID
        /// </summary>
        [DataMember]
        public string Id
        {
            get { return m_id; }
            set { m_id = value; }
        }
    }
}


IPersonManagerSerivce服务:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ContentTypes;

namespace BussinessServices
{
    [ServiceContract]
    //第二种方式,在契约上使用ServiceKwnoType,这是一种全局的方式
    //,如果在契约上标示过的子类,则在契约中的所有方法都可使用标示过的子类
    //[ServiceKnownType(typeof(Employee))]    
    public interface IPersonManagerService
    {
        [OperationContract]
        Person GetPerson();

        [OperationContract]
        //第三种方式,在方法上使用ServiceKnownType,这种方式限制在此方法上只能使用标示过的子类,若有其它子类
        //,但没有使用ServiceKnownType标示,则不能被使用,这种方式使用控制更加灵活
        [ServiceKnownType(typeof(Employee))]    
        void SetPerson(Person person);
    }

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class PersonManagerService : IPersonManagerService
    {
        private Person m_person;

        public Person GetPerson()
        {
            return m_person;
        }

        public void SetPerson(Person person)
        {
            m_person = person;
        }
    }

}


这个例子中的Host表上面例子一样,就不写了。

PersonEntry

客户端,主窗口内容与DataContract的例子一样,但是在按钮事件中,我们没有使用Person,而是使用的子类Employee:

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;

namespace PersonEntry
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        localhost.PersonManagerServiceClient proxy = new localhost.PersonManagerServiceClient();

        private void btn_Save_Click(object sender, EventArgs e)
        {
            localhost.Employee employee = new localhost.Employee();
            employee.SN = txt_SN.Text;
            employee.Sex = txt_Sex.Text;
            employee.Name = txt_Name.Text;

            proxy.SetPerson(employee);
            MessageBox.Show("info set sucess.");
        }

        private void btn_GetInfo_Click(object sender, EventArgs e)
        {
            var employee = proxy.GetPerson();
            if (employee != null)
            {
                txt_SN.Text = employee.SN;
                txt_Sex.Text = employee.Sex;
                txt_Name.Text = employee.Name;
                MessageBox.Show("info get sucess.");
                return;
            }
       
            MessageBox.Show("请先点击【保存】按钮,保存成功后,改变表单数据,再【获取】!");
        }
    }
}


运行这个例子可以发现,如果不使用KnownType指定子类,则客户端根本不会生成Employee,则肯定是无法使用的。

 

DEMO下载:WCF服务契约与复杂类型序列化DEMO

 

未完续:

【学习】WCF的服务契约、复杂类型序列化、消息契约的实现续-IXmlSerializable与MessageContract

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值