zoukankan      html  css  js  c++  java
  • 谈谈WCF中的Data Contract(4):WCF Data Contract Versioning

    软件工程是一门独特的工程艺术,需要解决的是不断改变的需求变化。而对于WCF,对于SOA,由于涉及的是对多个系统之间的交互问题,如何有效地解决不断改变的需求所带来的问题就显得更为重要:Service端版本的变化能否保持现有Consumer的正常调用,Consumer端的改变不至于影响对Service 的正常调用。对于Data Contract来说就是要解决这样的问题:Service端或者ClientData Type的改变不会影响Service的正常调用。

    在系统开发过程中,通过对Data Type添加额外的字段进而对其进行扩展,是一个种很常见的场景。本部分就作中介绍Data Contract的这种变化,Service或者ClientData Contract在本地添加一个新的Data Member会造成怎样的影响,WCF可以采用怎样的机制来解决这种单方面Data Contract版本的改变。

    我们同样通过Dome来说话。在这个Demo中,我使用上面介绍的Order Processing的场景,下面是整个Solution的结构(需要说明的是,本片文章提供的Code片断和Source Code都是基于VS 2008的)。

    1.   Service: Artech.DataContractVersioning.Service

    Data Contract

     

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

    namespace Artech.DataContractVersioning.Service
    {
        [DataContract(Namespace
    ="http://artech.datacontractversioning")]
        
    public class Order
        
    {
            [DataMember(Order 
    = 0)]
            
    public Guid OrderID
            
    {get;set;}

            [DataMember(Order 
    = 1)]
            
    public DateTime OrderDate
            
    getset; }

            [DataMember(Order 
    = 2)]
            
    public Guid SupplierID
            
    getset; }
        }

    }

    Service Contract Service Implementation: Process方法简单地将Order对象返回到客户端,当Client接受到Service返回的Order对象后,可以检测和由它传递给ServiceOrder对象有什么不同。

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

    namespace Artech.DataContractVersioning.Service
    {
        [ServiceContract]
        
    public interface IOrderManager
        
    {
            [OperationContract]
            Order Process(Order order);
        }

    }


    namespace Artech.DataContractVersioning.Service
    {
        
    public class OrderManagerService:IOrderManager
        
    {
            
    IOrderManager Members
        }

    }

    2.   Client端:

    Data Contract

        [DataContract(Name="Order",Namespace="http://artech.datacontractversioning")]
        
    public class CustomOrder
        
    {
            [DataMember(Order 
    = 0, Name="OrderID")]
            
    public Guid OrderNo
            
    getset; }

            [DataMember(Order 
    = 2, Name = "SupplierID")]
            
    public Guid SupplierNo
            
    getset; }

            [DataMember(Order 
    = 1)]
            
    public DateTime OrderDate
            
    getset; }        
        }

    }

    Program:先创建一个Order对象,向Console打印出Order的信息,随后以此作为参数调用Service,最后将返回的Order对象的信息打印出来,看看两者之间的有何区别。

    namespace Artech.DataContractVersioning.Client
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                ChannelFactory
    <IOrderManager> channelFactory = new ChannelFactory<IOrderManager>("orderManager.http");
                IOrderManager orderManager 
    = channelFactory.CreateChannel();

                
    try
                
    {
                    CustomOrder order 
    = new CustomOrder { OrderNo = Guid.NewGuid(), SupplierNo = Guid.NewGuid(), OrderDate = DateTime.Today, ShippingAddress="Room E101, Airport Rd #328, Suzhou Jiangsu Province" };
                    Console.WriteLine(
    "The original order: \n{0}", order.ToString());
                    order 
    = orderManager.Process(order);
                    Console.WriteLine(
    "\n\nThe order processed by service: \n{0}", order.ToString());
                }

                
    finally
                
    {
                    (orderManager 
    as IDisposable).Dispose();
                }


                Console.Read();
            }

        }

    }

    通过上面的分析,我们可以知道,尽管就CLR Type的定义来讲,Service端的OrderClient端的CustomOrder具有很大的差异,但是通过WCF Datacontract Attribute的适配,他们是相互匹配的。

    现在我们在Client端为Custom添加一个新的成员,ShippingAddress,通过重写ToString方法:

     

    namespace Artech.DataContractVersioning.Client
    {
        [DataContract(Name
    ="Order",Namespace="http://artech.datacontractversioning")]
        
    public class CustomOrder
        
    {
            [DataMember(Order 
    = 0, Name="OrderID")]
            
    public Guid OrderNo
            
    getset; }

            [DataMember(Order 
    = 2, Name = "SupplierID")]
            
    public Guid SupplierNo
            
    getset; }

            [DataMember(Order 
    = 1)]
            
    public DateTime OrderDate
            
    getset; }

            [DataMember(Order 
    = 3)]
            
    public string ShippingAddress
            
    getset; }
            
            
    public override string ToString()
            
    {
                
    return string.Format("Order No.\t: {0}\nSupplier No.\t: {1}\nOrder Date:\t: {2}\nShipping Address: {3}"this.OrderNo, this.SupplierNo, this.OrderDate, this.ShippingAddress);
            }

        }

    }

    我们来看看Client端程序运行的输出结果:

    通过上面的结果,我们发现Shipping Address的信息在经过Service处理后丢失了。原因很简单,Service端的Data Contract根本就没有ShippingAddress成员,所有在反序列化生成Order对象的时候将会忽略ShippingAddress的信息。

    其实这是一个不太合理的状况,对于Client来说,我指定了对象的某个对象的某个成员的值,结果Service处理返回后,却无缘无故(对于Client来说是无缘无故)丢失了。其实这种情况还出来在另一种场景之中:Client先调用Service AService B再将相同的对象作为参数调用Service C,现在假设ClientService BData ContractCustomOrderService AData Contract是少一个ShippingAddressOrder,那么经过Service A反序列化的对象将会是缺少Shipping AddressOrder对象,然后这个Order对象又由Service A传导Service B,虽然Service B能过识别Shipping Address成员,但是现在却没有改成员的值了,这显然是有问题的。我们把这样的问题称为Round trip问题,我们必须解决这样一个问题。

    其实在WCF中解决这样一个问题的方案简单而直接,那就是在Data Contract中定义一个额外的成员来存储没有在成员列表中定义的信息。我们可以让Data ContractData Type实现System.Runtime.Serialization.IExtensibleDataObject Interface来解决Round trip的版本问题。Interface的定义如下,他仅仅有一个Property成员:ExtensionData

    namespace System.Runtime.Serialization
    {
        
    // Summary:
        
    //     Provides a data structure to store extra data encountered by the System.Runtime.Serialization.XmlObjectSerializer
        
    //     during deserialization of a type marked with the System.Runtime.Serialization.DataContractAttribute
        
    //     attribute.
        public interface IExtensibleDataObject
        
    {
            
    // Summary:
            
    //     Gets or sets the structure that contains extra data.
            
    //
            
    // Returns:
            
    //     An System.Runtime.Serialization.ExtensionDataObject that contains data that
            
    //     is not recognized as belonging to the data contract.
            ExtensionDataObject ExtensionData getset; }
        }

    }

    现在我们来重新定义ServiceOrder Data Contract

    namespace Artech.DataContractVersioning.Service
    {
        [DataContract(Namespace
    ="http://artech.datacontractversioning")]
        
    public class Order:IExtensibleDataObject
        
    {
            [DataMember(Order 
    = 0)]
            
    public Guid OrderID
            
    {get;set;}

            [DataMember(Order 
    = 1)]
            
    public DateTime OrderDate
            
    getset; }

            [DataMember(Order 
    = 2)]
            
    public Guid SupplierID
            
    getset; }


            
    public ExtensionDataObject ExtensionData
            
    {
                
    get;
                
    set;
            }

        }

    }

    我们再来运行一下client端程序,我们发现现在没有数据丢失了:

    这就是实现了IExtensibleDataObject Interface的效果。就其本质,很简单,对于实现了该InterfaceData contract,将通过一个ExtensionDataObject 类型的对象来保存和获取那些没有在Data Contract定义的成员。为了一窥OrderExtensionData属性中保存的内容,我们在Service进行Debug,在QuickWatch中看看它是不是真的保存了不能识别的ShippingAddress

    [原创]谈谈WCF中的Data Contract(1):Data Contract Overview
    [原创]谈谈WCF中的Data Contract(2):WCF Data Contract对Generic的支持
    [原创]谈谈WCF中的Data Contract(3):WCF Data Contract对Collection & Dictionary的支持
    [原创]谈谈WCF中的Data Contract(4):WCF Data Contract Versioning

  • 相关阅读:
    【2018.05.05 C与C++基础】C++中的自动废料收集:概念与问题引入
    【2018.04.27 C与C++基础】关于switch-case及if-else的效率问题
    【2018.04.19 ROS机器人操作系统】机器人控制:运动规划、路径规划及轨迹规划简介之一
    March 11th, 2018 Week 11th Sunday
    March 10th, 2018 Week 10th Saturday
    March 09th, 2018 Week 10th Friday
    March 08th, 2018 Week 10th Thursday
    March 07th, 2018 Week 10th Wednesday
    ubantu之Git使用
    AMS分析 -- 启动过程
  • 原文地址:https://www.cnblogs.com/artech/p/974671.html
Copyright © 2011-2022 走看看