WCF开发之自定义序列化(IXmlSerializable)

这部分东西需要XML相关知识和序列化和反序列化的知识,下面我们根据一个Demo来理解一下自定义序列化的过程。

这个例子的程序结构和之前的都是一样的,唯一不同的我们这里没有使用DataContract,而是采用自己来编写序列化器的方法,为什么要自定义哪?

在完美的WCF世界中:
– 创建同业务对象描述相同功能的数据契约
– 暴露在服务契约中
在现实世界中:
– 可能无法拥有对象(不可序列化)
– 业务对象的实例化可能不正确
– 可能需要对现存的schema进行支持

如何自定义序列化气?他是怎样实现的那?

IXmlSerializable 类型为WSDL和元数据交换(MEX)提供了XSD schema
– 支持Contract-first
开发人员自己处理XML与业务对象之间的映射关系
– 需要具备XML的相关知识
– 进行适当的交验
• 在开销上较使用临时的数据契约少

IXmlSerializable
– 在服务契约中验证(类似数据契约)
在序列化过程中执行:
– IXmlSerializable.ReadXml()
– IXmlSerializable.WriteXml()
XmlSchemaProviderAttribute
– 改进IXmlSerializable.GetSchema()
– 为WSDL和MEX返回schema

看具体代码:

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

namespace ContentTypes
{
    
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;

        
public long Id
        {
            
get { return m_id; }
            
set { m_id = value; }
        }

        
public string Title
        {
            
get { return m_title; }
            
set { m_title = value; }
        }

        
public string Description
        {
            
get { return m_description; }
            
set { m_description = value; }
        }

        
public DateTime DateStart
        {
            
get { return m_dateStart; }
            
set { m_dateStart = value; }
        }

        
public DateTime DateEnd
        {
            
get { return m_dateEnd; }
            
set { m_dateEnd = value; }
        }

        
public string Url
        {
            
get { return m_url; }
            
set { m_url = value; }
        }
    }
}

 

上面的LinkItem没有使用DataContract和DataMember所以他目前只是一个普通的类,WCF不会为他自动创建序列化器,所以他不能再Server和Client之间直接进行传递。

下面开Service端的代码,这个文件就是我们自己定义的LinkItem的序列化器:

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

namespace WcfServiceLibraryDemo
{
    [XmlSchemaProvider(
"GetSchema")]
    
public class LinkItemSerializer : IXmlSerializable
    {
        
static string ns = "http://www.cnblogs.com/Charlesliu";
        
static string xs = "http://www.w3.org/2001/XMLSchema";

        
private LinkItem m_linkItem;

        
public LinkItem LinkItem
        {
            
get { return m_linkItem; }
            
set { m_linkItem = value; }
        }

        
public LinkItemSerializer()
        {
        }

        
public LinkItemSerializer(LinkItem item)
        {
            
this.m_linkItem = item;
        }

        
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='LinkItem'>" +
                
"<xs:sequence>" +
                
"<xs:element name='Id' type='xs:long' nillable='false'/>" +
                
"<xs:element name='Title' type='xs:string' nillable='false'/>" +
                
"<xs:element name='Description' type='xs:string' nillable='false'/>" +
                
"<xs:element name='DateStart' type='xs:dateTime' nillable='false'/>" +
                
"<xs:element name='DateEnd' type='xs:dateTime' nillable='true' minOccurs='0'/>" +
                
"<xs:element name='Url' type='xs:string' nillable='false' minOccurs='0'/>" +
                
"</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("LinkItem", ns);
        }

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

        
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
        {

            LinkItem item 
= new LinkItem();

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

                
if (reader.IsStartElement("Id"))
                {
                    reader.MoveToContent();
                    item.Id 
= int.Parse(reader.ReadString());
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                
else
                    
throw new XmlException("ExpectedElementMissing: Id element was expected.");

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

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

                
if (reader.IsStartElement("DateStart"))
                {
                    reader.MoveToContent();
                    reader.Read();
                    item.DateStart 
= reader.ReadContentAsDateTime();
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                
else
                    
throw new XmlException("ExpectedElementMissing: DateStart element was expected.");

                
if (reader.IsStartElement("DateEnd"))
                {
                    reader.MoveToContent();
                    reader.Read();
                    item.DateEnd 
= reader.ReadContentAsDateTime();
                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                
// optional

                
if (reader.IsStartElement("Url"))
                {
                    reader.MoveToContent();
                    item.Url 
= reader.ReadString();

                    reader.MoveToContent();
                    reader.ReadEndElement();
                }
                
else
                    
throw new XmlException("ExpectedElementMissing: Url element was expected.");

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


            
this.m_linkItem = item;
        }

        
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
        {

            writer.WriteStartElement(
"Id", ns);
            writer.WriteValue(m_linkItem.Id);
            writer.WriteEndElement();

            writer.WriteElementString(
"Title", ns, m_linkItem.Title);
            writer.WriteElementString(
"Description", ns, m_linkItem.Description);

            writer.WriteStartElement(
"DateStart", ns);
            writer.WriteValue(m_linkItem.DateStart);
            writer.WriteEndElement();

            writer.WriteStartElement(
"DateEnd", ns);
            writer.WriteValue(m_linkItem.DateEnd);
            writer.WriteEndElement();

            writer.WriteElementString(
"Url", ns, m_linkItem.Url);

        }
    }
}

 

下面详细讲解这段代码,首先对于自定义的序列化器一定要实现System.Xml.Serialization.IXmlSerializable接口,[XmlSchemaProvider("GetSchema")]用途是标示出提供Schema的对应方法名字,也就是说应该有一个GetSchema的方法与之对应。程序中通常要把LinkItem这个待序列化的Class定义为属性在序列化其中,以方便访问。对于IXmlSerializable这个接口,要实现下面3个方法:

XmlSchema GetSchema();

void ReadXml(XmlReader reader);

void WriteXml(XmlWriter writer);

实际应用中通常不处理GetSchema()这个方法,而是定义一个静态的方法GetSchema(XmlSchemaSet schemaSet)和[XmlSchemaProvider("GetSchema")]这个标签对应,上面代码的就是序列化的经典过程,其中GetSchema充当了生产XML结构的左右,Write和Read两个方法实现把对象数据写入xml中(序列化)和把xml中对象数据重新为给某对象(反序列化)。

注意此时,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(LinkItemSerializer item);

        [OperationContract(Name 
= "GetGig", Action = "http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGig", ReplyAction = "http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGigResponse")]
        LinkItemSerializer 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(LinkItemSerializer item)
        {
            m_linkItem 
= item.LinkItem;
        }

        
public LinkItemSerializer GetGig()
        {
            
return new LinkItemSerializer(m_linkItem);
        }

        
#endregion
    }
}

客户端的代码不变:

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 item 
= new LinkItem();

            item.Id 
= int.Parse(this.txtId.Text);
            item.Title 
= this.txtTitle.Text;
            item.Description 
= this.txtDescription.Text;
            item.DateStart 
= this.dtpStart.Value;
            item.DateEnd 
= this.dtpEnd.Value;
            item.Url 
= this.txtUrl.Text;

            m_proxy.SaveGig(item);
        }

        
private void cmdGet_Click(object sender, EventArgs e)
        {
            LinkItem item 
= m_proxy.GetGig();
            
if (item != null)
            {
                
this.txtId.Text = item.Id.ToString();
                
this.txtTitle.Text = item.Title;
                
this.txtDescription.Text = item.Description;

                
if (item.DateStart != DateTime.MinValue)
                    
this.dtpStart.Value = item.DateStart;
                
if (item.DateEnd != DateTime.MinValue)
                    
this.dtpEnd.Value = (DateTime)item.DateEnd;

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

 

Proxy 代码
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.4200
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace WinTest.MyServiceReference {
    
using System.Runtime.Serialization;
    
using System;
    
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.Runtime.Serialization""3.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name
="LinkItem", Namespace="http://www.cnblogs.com/Charlesliu")]
    [System.SerializableAttribute()]
    
public partial class LinkItem : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
        
        [System.NonSerializedAttribute()]
        
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
        
        
private long IdField;
        
        
private string TitleField;
        
        
private string DescriptionField;
        
        
private System.DateTime DateStartField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        
private System.Nullable<System.DateTime> DateEndField;
        
        [System.Runtime.Serialization.OptionalFieldAttribute()]
        
private string UrlField;
        
        [
global::System.ComponentModel.BrowsableAttribute(false)]
        
public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
            
get {
                
return this.extensionDataField;
            }
            
set {
                
this.extensionDataField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired
=true)]
        
public long Id {
            
get {
                
return this.IdField;
            }
            
set {
                
if ((this.IdField.Equals(value) != true)) {
                    
this.IdField = value;
                    
this.RaisePropertyChanged("Id");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired
=true, EmitDefaultValue=false)]
        
public string Title {
            
get {
                
return this.TitleField;
            }
            
set {
                
if ((object.ReferenceEquals(this.TitleField, value) != true)) {
                    
this.TitleField = value;
                    
this.RaisePropertyChanged("Title");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired
=true, EmitDefaultValue=false, Order=2)]
        
public string Description {
            
get {
                
return this.DescriptionField;
            }
            
set {
                
if ((object.ReferenceEquals(this.DescriptionField, value) != true)) {
                    
this.DescriptionField = value;
                    
this.RaisePropertyChanged("Description");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired
=true, Order=3)]
        
public System.DateTime DateStart {
            
get {
                
return this.DateStartField;
            }
            
set {
                
if ((this.DateStartField.Equals(value) != true)) {
                    
this.DateStartField = value;
                    
this.RaisePropertyChanged("DateStart");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(Order
=4)]
        
public System.Nullable<System.DateTime> DateEnd {
            
get {
                
return this.DateEndField;
            }
            
set {
                
if ((this.DateEndField.Equals(value) != true)) {
                    
this.DateEndField = value;
                    
this.RaisePropertyChanged("DateEnd");
                }
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue
=false, Order=5)]
        
public string Url {
            
get {
                
return this.UrlField;
            }
            
set {
                
if ((object.ReferenceEquals(this.UrlField, value) != true)) {
                    
this.UrlField = value;
                    
this.RaisePropertyChanged("Url");
                }
            }
        }
        
        
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        
        
protected void RaisePropertyChanged(string propertyName) {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged 
= this.PropertyChanged;
            
if ((propertyChanged != null)) {
                propertyChanged(
thisnew System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.ServiceModel""3.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(Namespace
="http://www.cnblogs.com/Charlesliu", ConfigurationName="MyServiceReference.GigManagerServiceContract", SessionMode=System.ServiceModel.SessionMode.Required)]
    
public interface GigManagerServiceContract {
        
        [System.ServiceModel.OperationContractAttribute(Action
="http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/SaveGig", ReplyAction="http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/SaveGigResponse")]
        
void SaveGig(WinTest.MyServiceReference.LinkItem item);
        
        [System.ServiceModel.OperationContractAttribute(Action
="http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGig", ReplyAction="http://www.cnblogs.com/Charlesliu/GigManagerServiceContract/GetGigResponse")]
        WinTest.MyServiceReference.LinkItem GetGig();
    }
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.ServiceModel""3.0.0.0")]
    
public interface GigManagerServiceContractChannel : WinTest.MyServiceReference.GigManagerServiceContract, System.ServiceModel.IClientChannel {
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute(
"System.ServiceModel""3.0.0.0")]
    
public partial class GigManagerServiceContractClient : System.ServiceModel.ClientBase<WinTest.MyServiceReference.GigManagerServiceContract>, WinTest.MyServiceReference.GigManagerServiceContract {
        
        
public GigManagerServiceContractClient() {
        }
        
        
public GigManagerServiceContractClient(string endpointConfigurationName) : 
                
base(endpointConfigurationName) {
        }
        
        
public GigManagerServiceContractClient(string endpointConfigurationName, string remoteAddress) : 
                
base(endpointConfigurationName, remoteAddress) {
        }
        
        
public GigManagerServiceContractClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
                
base(endpointConfigurationName, remoteAddress) {
        }
        
        
public GigManagerServiceContractClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                
base(binding, remoteAddress) {
        }
        
        
public void SaveGig(WinTest.MyServiceReference.LinkItem item) {
            
base.Channel.SaveGig(item);
        }
        
        
public WinTest.MyServiceReference.LinkItem GetGig() {
            
return base.Channel.GetGig();
        }
    }
}

自定义序列化的结果是通过VS工程无法正常的引用WCF服务,所以要用命令svcutil.exe http://localhost:9000/才能得到Proxy文件,将这个文件copy到客户端工程中就可以使用了。同时要把自动生成的配置文件也copy进去。

自定义序列化的给程序员提供了更加灵活的方式,因为现实世界中总是有这样那样的问题,有些时候无法用理想中的WCF的DataContract来完成。当然了,灵活了程序员就费劲了,但是不可否认这种方式的意义所在。(完)自定义序列化的给程序员提供了更加灵活的方式,因为现实世界中总是有这样那样的问题,有些时候无法用理想中的WCF的DataContract来完成。当然了,灵活了程序员就费劲了,但是不可否认这种方式的意义所在。(完)

更正个错误,在Service Class 里的LInkItem应该定义成static。

 

posted @ 2010-02-08 13:18  烟鬼  阅读(4861)  评论(2编辑  收藏  举报