WCF:返回具有基础对象的合同的派生对象(DataContractResolver)
我有 WCF 派生/基础合同问题。我有一个返回 BaseThing
对象的服务器接口/合约:
[OperationContract]
BaseThing Get_base_thing();
实现此对象的服务器有一个 DerivedThing
(派生自 BaseThing
),并且想要将其作为 BaseThing
返回。 如何告诉 WCF 我只想传输 DerivedThing
的 BaseThing
部分?
如果在 Get_base_thing
中,我只需返回对 DerivedThing
的引用,然后我得到一个 SerializationException
服务器端。
我想我需要定义一个 DataContractResolver,我查看了 MSDN 文章 使用数据契约解析器但这并不是 100% 清楚(至少对我来说)。
我的 DataContractResolver 应该如何告诉 WCF 仅传输我传递给它的派生对象的基本部分?
有没有什么方法可以通过 KnownType
属性更简单地做到这一点?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
KnownType 无法解决此问题。
听起来好像您在服务器上使用的对象模型和您正在使用的服务契约之间存在严重分歧。似乎有 3 种可能的解决方案:
1) 正如您所确定的数据契约解析器,使其在所有操作中自动执行。有很多示例,包括这个: http://blogs.msdn.com/b/youssefm/archive/2009/06/05/introducing-a-new-datacontractserializer-feature-the-datacontractresolver.aspx。
2) 调整您的对象模型以更好地匹配您的服务合同。也就是说,使用包含而不是继承来管理 BaseThing-DerivedThing 关系。这样,您就可以在服务器上使用 DerivedThing,并且只需通过网络返回 DerivedThing.BaseThing 即可。如果BaseThing需要从客户端传输到服务器,这也会工作得更好。
3)使用诸如 AutoMapper 之类的显式转换,因此在您的操作中很明显,服务器上使用的对象与暴露给外部世界的对象之间存在差异。
KnownType will not resolve this issue.
It sounds as if you have a serious divergence between the object model you're using at the server and the service contracts you're using. There seems to be 3 possible solutions:
1) Data Contract Resolver as you've identified to make it automatic across all your operations. There are a number of examples out there including this one: http://blogs.msdn.com/b/youssefm/archive/2009/06/05/introducing-a-new-datacontractserializer-feature-the-datacontractresolver.aspx.
2) Align your object model to better match your service contracts. That is, use containment rather than inheritance to manage the BaseThing-DerivedThing relationship. That way you work with DerivedThing at the server and simply return DerivedThing.BaseThing over the wire. If BaseThing needs to get transmitted from client to server, this will also work better.
3) Use explicit conversion using something like AutoMapper so it is obvious in your operations that there is a divergence between the objects being used at the server and those exposed to the outside world.
发布后我还发现了这个相同的问题 如何将派生类型序列化为基类。马克对我来说未被接受的第二个答案是解决这个问题的最简单的方法。即:
用装饰派生类
[DataContract(Name="BaseClass")]
请注意,此解决方案意味着派生将作为该对象的所有每种传输情况的基础进行传输。对我来说,这不是问题,如果是的话,您需要走 DataContractResolver 路线。
关于 DataContractResolver 路线的一些注释:
1. 这使您能够在某些调用中将派生作为派生传递,但在其他调用上作为派生传递 - 如果您需要这样做 - 如果不使用 about Name= 方法。
2. 我使用 datacontractrsolver 文章中的 DeserializeAsBaseResolver 时遇到异常,因为knownTypeResolver 返回 false。为了解决这个问题,我忽略了该调用的返回值,并始终从 TryResolveType 返回 true。这似乎有效。
3. 我最初认为,因为我们将序列化为基础,所以我不需要派生类上的 [DataContract]。那是错误的。该对象被序列化为派生对象并反序列化为基础对象 - 因此您必须使用 [DataContract] 装饰派生对象,但不要将任何字段标记为 [DataMembers] 以避免它们被不必要的序列化。
4. 如果您有命令行主机和服务主机,那么您需要代码在两者中插入合同解析器。我发现将其作为静态放在我的解析器中很有用。
5. 请注意,调用
cd.Operations.Find("Get_gateway_data")
中的“Get_gateway_data”字符串是返回相关对象的合约方法的名称。您需要为您想要此行为的每个呼叫执行此操作。此方法的最终代码:
主机代码(服务或命令行):
After posting I also found this SO identical question How to serialize a derived type as base. The unaccepted second answer by marc for me is the easiest way to resolve this issue. That is:
Decorate the derived class with
[DataContract(Name="BaseClass")]
Note that this solution means that derived will transport as base for all every case of transport of this object. For me that was not an issue if it is then you need to go the DataContractResolver route.
Some notes on the DataContractResolver route:
1. This enables you to pass the derived as derived on some calls but as base on other - if you need to do that - if not use about Name= approach.
2. I get an exception using the DeserializeAsBaseResolver from the datacontractrsolver article as it stands because the knownTypeResolver returns false. To fix that I ignor the return value of that call and always return true from TryResolveType. That seems to work.
3. I initially thought that because we were serializing as base that I didnt need [DataContract] on the derived class. That was wrong. The object is serialized as the derived object and derserialized as a base object - so you must decorate the derived with [DataContract] but don't mark any fields as [DataMembers] to avoid them being unnecessarily serialize.
4. If you have a command line host and a service host then you need the code to insert the contract resolver in both. I found it useful to put this as a static in my resolver.
5. Note that that the "Get_gateway_data" string in the call to
cd.Operations.Find("Get_gateway_data")
is the name of the contract method that returns the object concerned. You will need to do this for each call that you want this behaviour.Final code for this approach:
Host code (service or command line):