【问题标题】:Service - client interface, architecture advice服务 - 客户端接口,架构建议
【发布时间】:2009-12-15 13:50:58
【问题描述】:

我有一个 Windows WCF 服务和 Web 客户端。我的服务只有一种方法

[OperationContract]
SubmitOrder(OrderInfo info)....

// class used to pass all relevant data
[DataContract]
class OrderInfo
{
 [DataMember]
 OrderType Type;
 // general order data
}

在我引入新的订单类型(由OrderInfo.Type 属性控制)之前,这很棒。您可以将新订单类型视为源自一般订单(就行为而言)。 每个新订单都有一些附加属性。什么是最好的方法 实现 Order 的这种多态行为?

目前我只是在添加新订单的同时向 OrderInfo 类添加新属性。

[DataContract]
class OrderInfo
{
 [DataMember]
 OrderType Type;
 // general order data

 // First custom order data
 // Second custom order data
 // TODO - add new properties for new orders
}

我不太喜欢它,因为它太直了。如果我更改 [DataContract] 和客户端怎么办 是不是重建?

我有哪些选择?我当然可以实现继承并派生新的[DataContract]类,如MyCustomOrder1,但是序列化不支持继承,我需要使用[KnownTypes],由于某些原因,这是被禁止的。

【问题讨论】:

  • 嗯,它并不完全重复。我的问题是 - 如何在不使用 [KnownTypes] 的情况下使用新属性扩展基类。
  • 这是KnownType,而不是KnownTypes
  • 一种可能的解决方案是添加字符串属性并在客户端序列化您想要的任何内容并在服务端反序列化。

标签: c# design-patterns inheritance serialization polymorphism


【解决方案1】:

在我的脑海中,我不确定这是一个好主意,但我认为这样做的一种方法是放宽服务方面的合同,例如改用 MessageContract 并接受消息中的“任何”内容。您仍然可以将数据合同分发给您的客户,因此您可以根据模型对客户进行编程。在服务端,你需要弄清楚消息包含什么样的内容并采取相应的行动。

我不确定如何实现这一点的细节,但我会先查看 WCF 中的 Message 类:http://msdn.microsoft.com/en-us/library/ms734675.aspx

归结为使用“无类型”消息,如下所述:http://geekswithblogs.net/claeyskurt/archive/2008/09/24/125430.aspx,如前所述:WCF and Anonymous Types


按照本文http://geekswithblogs.net/claeyskurt/archive/2008/05/02/121848.aspx 的第2 部分所述,使用IExtensibleDataObject 是一种完全不同的方法(也许更简洁?)。


编辑:我正在阅读有关 data contract versioning 的信息,我想到了更好的解决方案

如果由于某种原因您不能使用 KnownType,那么您所做的就是创建新版本的合同。最简单的开始方法是

  • 使用子类型的所有属性创建单个 orderinfo 合同
  • 添加一个“类型”属性(一个字符串,因为以后不能再添加新的枚举,这将是一个重大更改)
  • 将基类中的所有属性设为“必需”
  • 将子类型上的所有属性设为“可选”
  • 实现 IExtensibleDataObject 以向前兼容
  • 在我们的服务中,使用 type 属性来确定它是什么类型的订单并采取相应的行动

现在,当您添加新类型时,将新属性添加到 OrderInfo 类中,只要它们是可选的并且类的其余部分不变,您将向后兼容您的客户还没有新版本的合同。是的,它可能会在客户端变得混乱,但您总是可以将其抽象到一些帮助类后面。

【讨论】:

    【解决方案2】:

    我需要使用 [KnownType] 这是 由于某些原因被禁止。

    禁止是什么意思?我使用KnownTypeAttribute 没有任何问题。这是一个例子。

    [DataContract]
    [KnownType( typeof( NetworkDeviceProperties ) )]
    public class DeviceProperties
    {
        [DataMember]
        public string MachineName { get; set; }
    }
    
    [DataContract]
    public class NetworkDeviceProperties : DeviceProperties
    {
        [DataMember]
        public IPAddress IPAddress { get; set; }
    }
    
    [ServiceContract]
    public interface ICollectionService
    {
        [OperationContract]
        [ServiceKnownType( typeof( NetworkDeviceProperties ) )]
        void Start( DeviceProperties properties );
    }
    

    在我的客户端,我创建了一个NetworkDeviceProperties 对象并将它毫无问题地传递给 Start() 方法。有关更多信息,请参阅此blog

    【讨论】: