WCF 客户端在识别 ServiceKnownTypes 时遇到问题?

标签 wcf datacontractserializer datacontract serviceknowntype

在将数据传回客户端时,我如何告诉 WCF 服务使用什么 KnownTypes?

我知道我可以使用 [ServiceKnownType]属性,这使得服务调用从 WCF 测试服务器运行良好,但它仍然从客户端失败。我在这里错过了什么吗?

[OperationContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
BaseClassZ GetObject();

来自客户端的错误消息是:

{"Element 'http://schemas.datacontract.org/2004/07/BaseClassZ' contains data from a type that maps to the name 'http://schemas.datacontract.org/2004/07/SubClassA'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'SubClassA' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer."}



使用 DataContractSerializer 和 KnownTypes 列表对 WCF 服务器上的对象进行序列化/反序列化工作正常。

更新:如果我将 KnownType 属性添加到基类,我似乎可以让客户端正确读取对象,但如果可能的话,我仍在寻找解决此问题的方法,因为基类用于很多项目而我没有想要在添加新项目时修改基类上的 KnownType 属性。
[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}

最佳答案

为避免阻止您的服务代码,请将已知类型放入服务的 web.config 中:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="SomeNs.BaseClassZ, SomeAssembly">
                <knownType type="SomeNs.SubClassA, SomeAssembly" />
                <knownType type="SomeNs.SubClassB, SomeAssembly" />
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

如果您想通过代码执行此操作,则需要在服务接口(interface)而不是操作方法上使用此属性,但我更喜欢声明性方法:
[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}

更新:

我提出了 sample project说明使用 web.config 来配置已知类型,这是我的首选方法。还有一个 sample project演示第二种方法。

更新 2:

使用 Silverlight 应用程序客户端查看更新后的代码后,我们注意到以下定义:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
public interface IService1 {

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

注意 BeginGetSingle方法包含已知的类型属性,而 BeginGetMany方法没有。事实上,这些属性应该放在服务定义中,以便类看起来像这样。
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassA))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyCommonLib.SubClassB))]
public interface IService1
{
    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetMany", ReplyAction="http://tempuri.org/IService1/GetManyResponse")]
    System.IAsyncResult BeginGetMany(System.AsyncCallback callback, object asyncState);

    System.Collections.ObjectModel.ObservableCollection<MyCommonLib.BaseClassZ> EndGetMany(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://tempuri.org/IService1/GetSingle", ReplyAction="http://tempuri.org/IService1/GetSingleResponse")]
    System.IAsyncResult BeginGetSingle(System.AsyncCallback callback, object asyncState);

    MyCommonLib.BaseClassZ EndGetSingle(System.IAsyncResult result);
}

由于这是一个自动生成的类,SLsvcUtil.exe 中可能存在错误。和 svcutil.exe 因为它表现出相同的行为。将已知的类型属性放在正确的位置可以解决问题。问题是这个类是由工具自动生成的,如果你尝试从 WSDL 重新生成它,它会再次搞砸。

因此,如果您具有以下服务定义,则似乎:
[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

并且这里使用的 3 个数据契约在导入服务定义时在客户端和服务器之间共享,返回集合的方法在生成的客户端代理中没有得到正确的已知类型属性。也许这是设计使然。

关于WCF 客户端在识别 ServiceKnownTypes 时遇到问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3840435/

相关文章:

c# - Data Contract Serializer - 如何省略集合的外部元素

c# DataContractSerializer 继承

c# - 创建 WCF ChannelFactory<T>

wcf - 为什么我的 WCF 服务不响应 web.config 中的 baseAddress 设置?

WCF 跨多个客户端共享 IClientMessageInspector 实例

c# - DataContractSerializer 序列化 List<T> 出错

c# - 来自数据库的 WCF Web json 服务?读数错误

WCF 数据契约与类序列化

c# - 我可以在 WCF ServiceContract 中公开数据成员吗?

c# - 可移植类库在 .NET 4.6 中没有 DataContract 或序列化函数