具有多态数组的WCF客户端调用方法失败

时间:2013-09-12 18:23:18

标签: c# .net wcf polymorphism

我有一个带有以下(示例)接口的WCF服务:

[ServiceContract]
[ServiceKnownType(typeof(Foo))]
[ServiceKnownType(typeof(Bar))]
[ServiceKnownType(typeof(Baz))]
public interface IMyInterface
{
    [OperationContract]
    void ProcessMessage(Message message);

    [OperationContract]
    void ProcessMessages(IEnumerable<Message> messages);
}

FooBarBaz都是Message的类型。

我可以使用ProcessMessage()FooBar对象从WCF客户端调用Baz,一切正常。但是,我无法使用数组(或列表或任何其他ProcessMessages(...))调用IEnumerable,因为这会失败:

  

尝试序列化参数时出错   http://tempuri.org/:messages。 InnerException消息是'Type   带有数据合同名称的'X.X.Foo'   'Foo:http://schemas.datacontract.org/2004/07/X.X'不是预料之中的。   考虑使用DataContractResolver或添加任何未知的类型   静态地到已知类型的列表 - 例如,通过使用   KnownTypeAttribute属性或通过将它们添加到已知列表中   传递给DataContractSerializer的类型。'。请参阅InnerException   了解更多详情。

当我查看生成的客户端代码reference.cs时,我看到了:

...
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyInterface/ProcessMessage", ReplyAction="http://tempuri.org/IMyInterface/ProcessMessageResponse")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Foo))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Bar))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(X.X.Baz))]
void ProcessMessage(X.X.Message message);

...
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyInterface/ProcessMessages", ReplyAction="http://tempuri.org/IMyInterface/ProcessMessagesResponse")]
void ProcessMessages(X.X.Message[] messages);

我注意到ServiceKnownTypeAttribute已添加到ProcessMessage但未添加到ProcessMessages。当我手动将同一组ServiceKnownTypeAttribute添加到ProcessMessages方法时,我从客户端使用包含FooBar和{{1}的数组调用它它工作正常。

如何让Visual Studio在第二种方法上生成这些属性?我的Baz错了吗?我是否将IMyInterface添加到了错误的位置?我哪里出错了?

修改 我应该提一下,[ServiceKnownType(typeof(...))]类是在一个“外部”程序集中(我可以控制,幸运的是),它打包在一个Nuget包中,而后者又在WCF服务和客户端中引用为此程序集启用了“重用类型...”选项。

1 个答案:

答案 0 :(得分:2)

由于您的类型位于不同的程序集中,并且/或者很难修改Message,因此您可以将其放在客户端程序集中以使其工作:

namespace X.X
{
    [KnownType(typeof(Foo))]
    public partial class Foo
    {
    }
    [KnownType(typeof(Bar))]
    public partial class Bar
    {
    }
    [KnownType(typeof(Baz))]
    public partial class Baz
    {
    }
}

(我知道你似乎在告诉它显而易见,但它确实有效。)

如果MessageFooBarBaz位于同一个程序集中,您只需将KnownType属性添加到{{1 class:

Message

然后你就不必为你的客户做任何特别的事了。

此外,我能够重现您的行为的唯一方法是将[KnownType(typeof(Foo))] [KnownType(typeof(Bar))] [KnownType(typeof(Baz))] public abstract class Message 放在自己的程序集中,从我的客户端和WCF库项目引用它,并使用“在引用的程序集中重用类型”复选框。如果这些类都是在客户端生成的,而不是一些被引用而一些是生成的,那么它的工作要简单得多,因为它会在Message上生成所有KnownType属性。