【问题标题】:WCF MessageContract InheritanceWCF 消息契约继承
【发布时间】:2009-08-24 09:12:51
【问题描述】:

我对 WCF 还很陌生,只是对如何正确获取 MessageContract 继承有疑问。我的设置的简化版本如下 - 一个“基本”消息类型,然后是另一个继承自它的“测试”消息。

[MessageContract]
public abstract class BaseMessage
{ }

[MessageContract]
public class TestMessage : BaseMessage
{ }

然后我在 ServiceContract 上有一个异步 OperationContract,定义为:

[OperationContract(AsyncPattern = true)]
IAsyncResult BeginFindRequest(BaseMessage request, AsyncCallback callback, object asyncState);

我遇到的问题是,当调用 BeginFindRequest 方法并为请求参数传入 TestMessage 实例时,WCF 框架将 TestMessage 实例反序列化为服务/服务器端的 BaseMessage。由于这被定义为一个抽象类,它会导致以下错误:

"消息无法反序列化 进入 MessageContract 类型 BaseMessage 因为它没有默认值 (无参数)构造函数。”

从我能找到的关于 MessageContract 继承的有限信息来看,它似乎应该可以工作。

所以我的问题是 - 为了让它发挥作用,我缺少什么;或者我是否应该在 ServiceContract 上专门为该类型定义一个单独的 OperationContract - 缺点是我最终可能会得到许多额外的 OperationContract?

【问题讨论】:

  • PS - 我正在使用 net.tcp 和 NetDataContractSerializer(在客户端/服务器上)。
  • 嘿弗兰克 - 使用它上面的“代码按钮”(带有 010101010110 的那个) - 格式化您的代码部分。它可以很好地缩进它们,并且语法会突出显示它们 - 强烈推荐!
  • 谢谢 Marc,想知道你是怎么做到的!
  • @Frank: 黑魔法和巫术:-)

标签: wcf inheritance messagecontract operationcontract


【解决方案1】:

最后我发现这篇博文一针见血 -

不幸的是,合同的方式 用 WCF 表示使得非常 很容易忘记他们的目的是什么: 定义发送到的消息 操作并从 手术。实际上你必须 想“我将如何表达这些数据 在 XML 中?”。 XML 不支持 继承所以无论你放什么 合同将不得不有一些 映射到 XML 的方式。数据 用于定义消息的合约 只是一个 .NET 类型的便利 用于为数据生成 XML 你想通过——如果你查看它们 任何其他方式你注定 痛苦的世界。所以想想数据 你想通过,而不是它可能如何 碰巧出现在你的 业务层并设计您的 相应的 DataContracts。

http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,a3775eb1-b441-43ad-b9f1-e4aaba404235.aspx

因此,我将进行重构以提供具有显式合同类型的附加方法。这也将允许我通过删除所有类型检查来清理服务实现。

感谢您的帮助。

【讨论】:

  • 是的,是的——“OO”世界和“SOA”世界有时是完全不同的野兽......基于 XML 的消息传递不适合继承和其他 OO 主义我们已经习惯了......
【解决方案2】:

好的,第一个问题是:你为什么真的使用消息合约?你真的需要那个吗??

通常,仅当您需要严格控制 SOAP 消息的布局时才使用消息协定,例如为了满足您需要调用的需要特定标头等的遗留系统。

“正常”的 WCF 调用几乎不需要使用消息协定。

您使用[ServiceContract] 定义您的服务调用(您的服务上的方法),并将数据结构作为[DataContract] 传递。如果你有一个 DataContract,你有更多的选择来处理你的服务中的继承/多态性(比消息契约结构更多)。

马克

【讨论】:

  • 我选择 MessageContract 的原因是阅读了 WCF 最佳实践文章,我记得该文章建议在 DataContracts 上使用它们可以减少消息“膨胀”。我现在正在尝试查找要参考的文章。我将尝试转换为 DataContract 以查看是否可以立即解决问题。谢谢马克
  • designpatternsfor.net/default.aspx?pid=99 - 从清单 5 开始(服务消息模式)。
  • 感谢您的链接; Rob 在这篇文章中提出的观点对我来说似乎有点“太高级”了。在大多数情况下,您不会真正关心他批评的这种“双重包装”。再说一遍:如果你真的必须控制确切的消息布局,那么是的 - 使用消息契约。但对我来说,这是一种最后的手段,如果没有其他方法的话。我不建议将其作为默认做法。
  • marc 在这里真的一针见血:f.ex Juval Lowys WCF 标准说:“避免使用消息合同”是有原因的,甚至没有开始在他关于该主题的书中描述它们。
  • 文件流上传/下载不需要消息合约吗?
【解决方案3】:

是否可以将 BaseMessage 更改为具有无参数构造函数的具体类?

错误信息告诉我们无法初始化BaseMessage类型的对象,因为它是抽象的。

【讨论】:

  • 我已经尝试过了,但随后消息被反序列化为该类型,因此丢失了 TestMessage 类型信息。 BeginFindRequest 当前根据消息的类型应用逻辑,这意味着逻辑不会执行,因为类型是 BaseMessage。
【解决方案4】:

该错误只是希望您拥有一个可以使用的默认空构造函数。但是,我同意 marc_s;在我从事的项目中,我很少使用消息契约,我记得的唯一情况是作为文件传输服务的一部分,其中文件块在消息中传递。

【讨论】:

  • 根据我对 codemeit 的评论,删除 abstract 关键字可以使反序列化工作,但是当我应该有一个 TestMessage 对象时,我只剩下一个 BaseMessage 对象。我将转换为 DataContract 以查看它是否可以解决问题。
  • 你能保持抽象,但给 BaseMessage 一个受保护的默认构造函数?
【解决方案5】:

尝试使用KnownType 属性装饰您的[ServiceContract]。由于 TestMessage 在公共操作中不是“可见的”,这有助于管道在看到它时知道如何处理它。

如果这应该允许 [DataContract] 被序列化为 TestMessage 您仍然可能需要通过 'is a' 或其他一些转换以不同方式处理多条消息。

【讨论】:

  • 已经尝试过没有帮助的 KnownType 和 ServiceKnownType 属性,但由于我使用的是 NetDataContractSerializer,这应该不是必需的?
  • 有趣..不,据我了解,您不应该需要 KnownType/ServiceKnownType,但这也意味着如果不使用 KnownType/ServiceKnownType,您在某处共享一个带有声明的 TestMessage 的通用 dll?否则,客户端不会知道任何关于 TestMessage 的信息,只有 BaseMessage - 只是假设我在 NetDataContractSerializer 方面的经验为零 - 我可能不得不更加努力地寻找需要它来获得一些经验! :) 你如何替换默认的 DataContractSerializer?您是在服务开放之前更换它吗?
  • 是的,所有必需的库都在服务器/客户端之间共享。在替换 DataContractSerializer 方面,我在各种可用的基于属性的解决方案上采用了工厂方法类型方法 - 这样我就不必在任何地方放置更多属性。这基本上返回了一个服务主机/代理,其中所有 DataContractSerializerOperationBehaviors 都替换为我自己的 NetDataContractSerializerOperationBehaviour - 它在需要时提供了一个 NetDataContractSerializer。
猜你喜欢
  • 1970-01-01
  • 2011-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-27
相关资源
最近更新 更多