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

本文关键字:客户端 调用 方法 失败 WCF 多态 数组 | 更新日期: 2023-09-27 18:31:10

我有一个具有以下(示例)接口的 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

我可以从带有FooBarBaz对象的 WCF 客户端调用ProcessMessage(),一切正常。但是,我不能用数组(或列表或任何其他IEnumerable)调用ProcessMessages(...),因为这会失败:

尝试序列化参数时出错 http://tempuri.org/:messages。内部异常消息是"类型 具有数据协定名称的"X.X.Foo" "Foo:http://schemas.datacontract.org/2004/07/X.X"不是预期的。 考虑使用 DataContract Resolver 或添加任何未知的类型 静态到已知类型的列表 - 例如,通过使用 已知类型属性或将它们添加到已知列表中 传递给 DataContractSerializer 的类型。 请参阅内部异常 了解更多详情。

当我查看生成的客户端代码时,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 s 添加到 ProcessMessages 方法中时,我使用包含 Foo 的数组从客户端调用它,BarBaz它工作正常。

如何让Visual Studio在第二种方法上生成这些属性?我的IMyInterface错了吗?我是否在错误的位置添加了[ServiceKnownType(typeof(...))]?我哪里做错了?

编辑也许,我应该提到Message类位于一个"外部"程序集中(幸运的是,我可以控制它),我将其打包在 Nuget 包中,而 Nuget 包又在 WCF 服务和客户端中引用了"重用类型..."为此程序集启用选项。

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

由于您的类型位于不同的程序集中,并且/或很难修改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 属性添加到Message类中:

[KnownType(typeof(Foo))]
[KnownType(typeof(Bar))]
[KnownType(typeof(Baz))]
public abstract class Message

那么你就不必对你的客户做任何特别的事情了。

此外,我能够重现您的行为的唯一方法是将Message放在其自己的程序集中,从我的客户端和 WCF 库项目中引用该程序集,并使用"在引用的程序集中重用类型"复选框。如果类都是在客户端生成的,而不是一些被引用和一些生成,它的工作方式要简单得多,因为它在Message上生成所有KnownType属性。