【问题标题】:.NET asmx web services: serialize object property as string property to support versioning.NET asmx Web 服务:将对象属性序列化为字符串属性以支持版本控制
【发布时间】:2010-04-02 20:52:12
【问题描述】:

我正在升级我们的网络服务以支持版本控制。我们将发布我们的版本化网络服务,如下所示:

http://localhost/project/services/1.0/service.asmx
http://localhost/project/services/1.1/service.asmx

此版本控制的一个要求是不允许我破坏原始 wsdl(1.0 wsdl)。挑战在于如何通过 Web 服务背后的逻辑(该逻辑包括许多命令和适配器类)来管理新版本化的类。请注意,目前无法升级到 WCF。

为了说明这一点,让我们以博客和帖子为例。在引入版本之前,我们传递的是具体的对象而不是接口。所以AddPostToBlog 命令会接收Post 对象而不是IPost

// Old AddPostToBlog constructor.
public AddPostToBlog(Blog blog, Post post) {
    // constructor body
}

随着版本控制的引入,我想在保留原来的Post 的同时添加一个PostOnePointOnePostPostOnePointOne 都将实现 IPost 接口(它们没有扩展抽象类,因为继承破坏了 wsdl,尽管我想可能有办法通过一些花哨的 xml 序列化技巧来解决这个问题)。

// New AddPostToBlog constructor.
public AddPostToBlog(Blog blog, IPost post) {
    // constructor body
}

这让我们想到了关于序列化的问题。原始的Post 类有一个名为Type 的枚举属性。对于各种跨平台兼容性问题,我们正在将 Web 服务中的枚举更改为字符串。所以我想做以下事情:

// New IPost interface.
public interface IPost
{
    object Type { get; set; }
}

// Original Post object.
public Post
{
    // The purpose of this attribute would be to maintain how
    // the enum currently is serialized even though now the
    // type is an object instead of an enum (internally the
    // object actually is an enum here, but it is exposed as
    // an object to implement the interface).
    [XmlMagic(SerializeAsEnum)]
    object Type { get; set; }
}

// New version of Post object
public PostOnePointOne
{
    // The purpose of this attribute would be to force
    // serialization as a string even though it is an object.
    [XmlMagic(SerializeAsString)]
    object Type { get; set; }
}

XmlMagic 指的是XmlAttribute 或 System.Xml 命名空间的其他部分,它允许我控制正在序列化的对象属性的类型(取决于我正在序列化的对象的版本)。

有谁知道如何做到这一点?

【问题讨论】:

    标签: c# .net web-services xml-serialization asmx


    【解决方案1】:

    我不清楚你想要什么,IPost 和 Post 等等。

    您似乎想更新您的网络服务。您使用“版本”一词。我不清楚的是外部界面发生了什么变化。你没有这样描述。

    我们都同意服务的内部实现对服务的消费者应该是不透明的,对吧?因此,无论您是否更新了您的实现,该服务的客户都不应该知道。无论您使用 IPost 还是 Post 或 IWhatever,客户都不能关心,不能意识到。对服务消费者来说唯一重要的是在线签名——公共接口。 WSDL。

    你已经说过你不允许破坏原来的 WSDL。我不知道那是什么意思。这是否意味着

    • 新服务必须使用相同的 WSDL
    • 新服务可以使用不同的 WSDL,但旧客户端必须继续运行?
    • 还有别的吗?

    通常,当人们谈到对 Web 服务进行版本控制时,他们的意思是更改通过网络发送的消息。

    版本 1 可能如下所示:

    <Post xmlns="http://www.example.com/webservices/2010/04">
      <AuthorId>217</AuthorId>
      <Posted>2010-04-07T22:02:23.2214747Z</Posted>
      <Title>Hello, I must be going.</Title>
      <Content>...</Content>
      <Type>Rant</Type>
    </Post>
    

    为了保持兼容,版本 2 只能添加元素。例如,在消息中添加LastEdited 元素:

    <Post xmlns="http://www.example.com/webservices/2010/04">
      <AuthorId>217</AuthorId>
      <Posted>2010-04-07T22:02:23.2214747Z</Posted>
      <LastEdited>2010-04-07T22:02:23.2214747Z</LastEdited>
      <Title>This is Getting Very Interesting</Title>
      <Content>...</Content>
      <Type>Rant</Type>
    </Post>
    

    当我说兼容时,我的意思是像上面这样的版本 1 文档将可以被版本 2 服务反序列化。

    你在做这样的事情吗?如果是这样,请详细说明。

    你没有描述任何这些。您的描述仅关注服务的内部实现。您还没有提及外部可访问接口,这是对服务进行“版本化”时主要关注的领域。


    如果您只是想要一种将属性序列化为字符串的方法,并且该属性的类型没有合适的 ToString() 实现,则可以使用代理属性。该模式的工作原理如下:用[XmlIgnore] 标记实际属性。使用读取或更新实际属性的 getter 和 setter 创建一个额外的 String 类型的“代理”属性。像这样:

    [XmlRoot("Post", Namespace="http://www.example.com/webservices/2010/04")]
    public class Post1_1
    {
        public Int32 AuthorId { get; set; }
        public DateTime Posted { get; set; }
        public String Title { get; set; }
        public PostType @Type { get; set; }
    
        [XmlIgnore]
        public DateTime LastEdited { get; set; }
    
        [XmlElement("LastEdited")]
        public String LastEdited_Surrogate
        {
            get
            {
                if (LastEdited > (DateTime.UtcNow - new TimeSpan(24,0,0)))
                    return "Today";
                else if (LastEdited > (DateTime.UtcNow - new TimeSpan(48,0,0)))
                    return "Yesterday";
                else
                    return LastEdited.ToString();
            }
    
            set
            {
                if (value == "Today")
                    LastEdited = DateTime.UtcNow;
                else if (value == "Yesterday")
                    LastEdited = DateTime.UtcNow - new TimeSpan(24,0,0);
                else
                    LastEdited = DateTime.Parse(value);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-16
      • 1970-01-01
      • 2017-05-03
      • 1970-01-01
      • 1970-01-01
      • 2013-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多