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

接前面

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

这帖实践对应

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

 

IXmlSerializable

  • IXmlSerializable类型为WSDL和元数据交换(MEX)提供了XSD schema
    • 支持Contract-first
    • 在服务契约中验证(类似数据契约DataContract)
  • 开发人员自己处理XML与业务对象之间的映射关系
    • 需要具备XML的相关知识
    • 进行适当的检验
  • 在开销上较使用临时的数据契约少
  • 在序列化过程中执行:
    • IXmlSerializable.ReadXml() 返序列化时调用
    • IXmlSerializable.WriteXml()  序列化时调用
  • XmlSchemaProViderAttribute
    • 改进IXmlSerializable.GetSchema() 当客户端或浏览者访问schema时,会调用GetSchema方法
    • 为WSDL和MEX返回schema

 

在这个实践中,直接使用了VS2010中的WCF Service库来替代了前面例子中的BussinessServices与Host两个项目,通过WCF Service 库创建服务后,不需要像前面那种方式需要自己手动先运行Host,更加的方便。

 

项目结构:

 

Person.cs

实体类中没有使用任何可序列化的标记,意味这个类本来是不可以被序列化的

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

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

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

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

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

 

PersonSerializer

实现Xml序列化

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Schema;
using System.Xml.Serialization;
using ContentTypes;
using System.Xml;
using System.IO;

namespace WCFServiceLibrary
{
    [XmlSchemaProvider("GetSchema")]
    public class PersonSerializer : IXmlSerializable
    {
        static string ns = "http://blog.csdn.net/llxchen";
        static string xs = "http://www.w3.org/2001/XMLSchema";

        private Person m_person;
        public Person Person
        {
            get { return m_person; }
            set { m_person = value; }
        }

        public PersonSerializer() { }

        public PersonSerializer(Person person)
        {
            m_person = person;
        }

        public static XmlQualifiedName GetSchema(XmlSchemaSet schemaSet)
        {
            string schemaString = String.Format(
                "<xs:schema xmlns:tns='{0}' xmlns:xs='{1}' targetNamespace='{0}' elementFormDefault='qualified' attributeFormDefault='unqualified'>" 
                + "<xs:complexType name='Person'>" 
                    + "<xs:sequence>"
                            + "<xs:element name='SN' type='xs:string' nillable='false'/>"
                            + "<xs:element name='Name' type='xs:string' nillable='false'/>"
                            + "<xs:element name='Sex' type='xs:string' nillable='false'/>" 
                    + "</xs:sequence>" 
                + "</xs:complexType>" 
                + "</xs:schema>", ns, xs);

            XmlSchema schema = XmlSchema.Read(new StringReader(schemaString), null);
            schemaSet.XmlResolver = new XmlUrlResolver();
            schemaSet.Add(schema);

            return new XmlQualifiedName("Person", ns);

        }

        public XmlSchema GetSchema()
        {
            throw new NotImplementedException("IXmlSerializable.GetSchema() is not implemented. Use static GetSchema() instead.");
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            Person person = new Person();

            while (reader.IsStartElement())
            {
                reader.MoveToContent();
                reader.Read();

                if (reader.IsStartElement("SN"))
                {
                    reader.MoveToContent();
                    person.SN = reader.ReadString();
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                else
                    throw new XmlException("ExpectedElementMissing: SN element was expected.");

                if (reader.IsStartElement("Name"))
                {
                    reader.MoveToContent();
                    person.Name = reader.ReadString();
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                else
                    throw new XmlException("ExpectedElementMissing: Name element was expected.");

                if (reader.IsStartElement("Sex"))
                {
                    reader.MoveToContent();
                    person.Sex = reader.ReadString();
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                else
                    throw new XmlException("ExpectedElementMissing: Sex element was expected.");

                reader.MoveToContent();
                reader.ReadEndElement();
            }

            this.m_person = person;
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteElementString("SN", ns, m_person.SN);
            writer.WriteElementString("Name", ns, m_person.Name);
            writer.WriteElementString("Sex", ns, m_person.Sex);
        }
    }
}


IPersonManagerService

服务契约,注意这里的传递的参数类型与返回类型已经变成了我们的PersonSerializer

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

namespace WCFServiceLibrary
{
    [ServiceContract(Namespace = "http://blog.csdn.net/llxchen", SessionMode = SessionMode.Required)]
    public interface IPersonManagerService
    {
        [OperationContract(Action = "http://blog.csdn.net/llxchen/GetPerson")]
        PersonSerializer GetPerson();

        [OperationContract(Action = "http://blog.csdn.net/llxchen/SetPerson")]
        void SetPerson(PersonSerializer person);
    }   
}

 

PersonManagerService

实现契约

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

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

        public PersonSerializer GetPerson()
        {
            return new PersonSerializer(m_person);
        }

        public void SetPerson(PersonSerializer personSerializer)
        {
            m_person = personSerializer.Person;
        }
    }
}


客户端

代码与前面例子没有任何变化,意味着我们即使通过IXmlSerializable这种方式实现了序列化,但对客户端没有任何影响。

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("请先点击【保存】按钮,保存成功后,改变表单数据,再【获取】!");
        }
    }
}


 

消息契约(MessageContract)

  • MessageContractAttribute
    • 对控制消息头和消息元素提供强力支持
  • 所支持的属性
    • MessageHeaderAttribute
      • 应用到消息契约的域(fields)或者(properties)
        • 为创建自定义头提供了简单的方法
      • 能够提供Name,NameSpace,ProtectionLevel
      • 可以设置SOAP协议的设置:Relay,Actor,MustUnderstand
    • MessageBodyMemberAttribute
      • 应用到消息契约的域(fields)或者属性(properties)
      • 能够拥有多个body元素
        • 等价于在操作中拥有多个参数
        • 返回多个复杂类型数据的唯一方法
      • 总是提供顺序(Order)
      • 可以设置Name,Namespace,ProtectionLevel
  • 用于
    • 添加自定义头(custom headers)
    • 控制消息是否被包装
    • 控制签名与加密

实践:

 

项目结构:

 

Person.cs

使用的DataContract方式序列化:

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; }
        }
    }
}

 

Message.cs

定义MessageContract,其中定义了SetPersonRequest与SetPersonResponse对应契约的SetPerson方法,GetPersonRequest与GetPersonResponse对应GetPerson方法:

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

namespace WCFServiceLibrary
{
    [MessageContract(IsWrapped=false)]
    public class SetPersonRequest
    {
        private Person m_person;

        
        [MessageBodyMember]                 //定义了一个body
        public Person Person
        {
            get { return m_person; }
            set { m_person = value; }
        }
    }

    [MessageContract(IsWrapped=false)]
    public class SetPersonResponse
    {
    }

    [MessageContract(IsWrapped=false)]
    public class GetPersonRequest
    {
        private string m_licenseKey;
        
        [MessageHeader]
        public string LicenseKey
        {
            get { return m_licenseKey; }
            set { m_licenseKey = value; }
        }
    }

    [MessageContract(IsWrapped=false)]
    public class GetPersonResponse
    {
        private Person m_person;

        public GetPersonResponse() { }

        public GetPersonResponse(Person person)
        {
            m_person = person;
        }

        [MessageBodyMember]
        public Person Person
        {
            get { return m_person; }
            set { m_person = value; }
        }
    }
}


总结一点:凡是定义在Request中有[MessageHeader]或[MessageBody]的那些属性,它们就是在客户端调用服务相应方法的参数。

 

IPersonManagerService.cs

定义契约,在使用MessageContract时,传入的参数应该是Request,返回则是对应的Response

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

namespace WCFServiceLibrary
{
    [ServiceContract(Namespace = "http://blog.csdn.net/llxchen", SessionMode = SessionMode.Required)]
    public interface IPersonManagerService
    {
        [OperationContract(Action = "http://blog.csdn.net/llxchen/GetPerson")]
        GetPersonResponse GetPerson(GetPersonRequest request);

        [OperationContract(Action = "http://blog.csdn.net/llxchen/SetPerson")]
        SetPersonResponse SetPerson(SetPersonRequest request);
    }   
}


 

PersonManagerService.cs

实现契约:

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

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

        public GetPersonResponse GetPerson(GetPersonRequest request)
        {
            if (request.LicenseKey != "xxx")
            {
                throw new FaultException("Invalid license key.");
            }
            return new GetPersonResponse(m_person);
        }

        public SetPersonResponse SetPerson(SetPersonRequest request)
        {
            m_person = request.Person;
            return new SetPersonResponse();
        }
    }
}


客户端:

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("xxx");
            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("请先点击【保存】按钮,保存成功后,改变表单数据,再【获取】!");
        }
    }
}

 

MessageContractAttribute 类型公开以下成员

 

 

DEMO下载地址:http://download.csdn.net/detail/llxchen/3580147

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值