【问题标题】:WCF trouble with Types?类型的 WCF 问题?
【发布时间】:2013-06-17 10:01:02
【问题描述】:

当我调用我的 WCF Soap 服务的方法时,抛出一个错误,并且在 svlog 文件中出现错误:

不应使用数据协定名称“Message:http://schemas.datacontract.org/2004/07/xxx.ActiveDirectoryService.classes.WCF”键入“xxx.ActiveDirectoryService.classes.WCF.Message”。考虑使用 DataContractResolver 或将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。

我尝试在这里和那里使用 KnownType,但没有成功(我必须承认我不太确定我是否 100% 正确使用它)。

这是我的接口/类:

[ServiceContract]
public interface IActiveDirectory
{
    [OperationContract]
    [WebGet]
    void Dummy();

    [OperationContract]
    [WebGet]
    AbstractMessage Dummy2();

    [OperationContract]
    [WebGet]
    AbstractMessage Dummy3();

    [OperationContract]
    [WebGet]
    AbstractMessage SetPassWord(string customer, string customerPassword, string userLogin, string userPassword);
}

[DataContract]
public abstract class AbstractMessage
{
    [DataMember]
    public virtual bool IsError { get; set; }

    [DataMember]
    public virtual string ErrorMessage { get; set; }

    [DataMember]
    public virtual string ReturnValue { get; set; }
}

public class Message : AbstractMessage
{
<...>
}


[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
[KnownType(typeof(AbstractMessage))]
public class ActiveDirectory : IActiveDirectory
{
    public void Dummy()
    {
    }

    public AbstractMessage Dummy2()
    {
        return new AbstractMessage();
    }

    public AbstractMessage Dummy3()
    {
        return new Message();
    }

    public AbstractMessage SetPassWord(string customer, string customerPassword, string userLogin, string userPassword)
    {
    <...>

        return message; // message is of type Message
    }
}

编辑:12AM35 GMT+1

我添加了 Dummy() 方法。

  • 从客户端调用 Dummy 工作正常。
  • 从客户端调用 Dummy2 工作正常。
  • 从客户端调用 Dummy3 会导致同样的错误。

编辑 12AM39 GMT+1

进行以下更改没有帮助。

[DataContract]
[KnownType(typeof(AbstractMessage))]
public class Message : AbstractMessage

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
[KnownType(typeof(AbstractMessage))]
[KnownType(typeof(Message))]
public class ActiveDirectory : IActiveDirectory

编辑:13AM31 GMT+1

如果我将 Dummy3 返回类型设置为 Message,则在客户端代码中调用 Dummy3 会起作用。
WCF + 多态性有些奇怪...

【问题讨论】:

    标签: c# wcf serialization types


    【解决方案1】:

    您的 WCF 服务需要返回 DTO。这些不能是接口,如果你想返回抽象基类,那么你需要告诉数据协定序列化器你实际上打算返回这些类型的哪些实现。

    它已经知道 AbstractMessage 类型(作为其操作契约的一部分),但是该类型的所有未在操作契约中明确声明的实现都应在已知类型中声明。

    尝试添加这个:

    [ServiceContract]
    [ServiceKnownType(typeof(Message))]
    public interface IActiveDirectory
    {
        ...
    }
    

    在这里,您告诉数据协定序列化程序,此服务可能会返回(或期望)Message 类型的对象作为其方法的参数。

    这应该也可以:

    [DataContract]
    [KnownType(typeof(Message))]
    public abstract class AbstractMessage
    {
        ...
    }
    

    因为您想告诉数据协定序列化程序 MessageAbstractMessage 的已知类型

    我相信您的更改不起作用,因为您在服务上使用 KnownTypes 而不是 ServiceKnownTypes,并且您尝试在派生类而不是父类上应用已知类型,这是您在语言中习惯做的事情(Message 是 AbstractMessage),但在 WCF 中,您必须将其翻转并将派生的实现放在父类上(AbstractMessage 具有实现 Message),这可能会受到限制。

    你说:There 's something odd with WCF + Polymorphism...

    相信我,最好不要将 WCF 视为支持多态性,因为事实并非如此。您只需返回 DTO,如果您想尝试使这些多态,您会遇到很多问题。多态性是使用接口实现的,您不能在 WCF 中使用接口。如果您使用抽象类,那么由于 c# 中缺乏多重继承,您很快就会意识到 DTO 只能表示对象的单个视图,并且您无法创建表示域模型类的多个接口的 DTO。 (见this question的主题)

    请参阅我的answer here,详细了解如何通过KnownTypesServiceKnownTypes 或配置传递已知类型的知识。

    【讨论】:

    • [KnownType(typeof(Message))] 工作正常,但我必须更新客户的参考资料(直到几分钟前我才这样做)。
    【解决方案2】:
    [KnownType(typeof(AbstractMessage))]
    

    编译器已经发现您返回的是 AbstractMessage。从描述中无法弄清楚的是从 AbstractMessage 派生的实例。这些是你需要知道的类型。从 AbstractMessage 派生并返回的任何类型都必须是已知类型,并且需要在 AbstractMessage 本身上声明

    [KnownType(typeof(DerivedMessage1))]
    [KnownType(typeof(DerivedMessage2))]
    [KnownType(typeof(DerivedMessage3))]
    

    在您的情况下,您需要该属性告诉数据协定序列化程序:“嘿,如您所见,我将返回一个 AbstractMessage,但您不可能知道:实际上,它是一个 Message”:

    [ServiceBehavior(...)]
    [KnownType(typeof(Message))]
    public class ActiveDirectory : IActiveDirectory
    {
        public AbstractMessage Dummy3()
        {
            return new Message();
        }
    }
    

    【讨论】:

    • 您能否检查我 12:39 的编辑,因为我反映了我对您的回答的理解。
    • 看起来不错。您永远不需要 AbstractMessage 作为已知类型,因为可以从方法签名中推断出该类型。当服务类处理不包含在 System 命名空间或方法签名本身中的类型时,您只需要该服务类的 KnownType 属性。
    • @Serge 尝试将KnownType(...) 直接添加到ServiceContract(在您的情况下为IActiveDirectory
    • @JensKloster 我认为如果您将ServiceKnownType() 添加到服务合同中,它才能正常工作。
    • @SamHolder 我修改了service后面的属性,ServiceKnownType属性只在合约接口有效,实现需要KnownTypeAttribute。您帖子中的示例是正确的。
    【解决方案3】:

    我曾经遇到过这个问题。就我而言,据我所知,wcf 序列化程序需要非抽象类型才能正常工作。所以在我的例子中,从抽象类和接口切换到普通对象就可以了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多