WCF客户端无法识别ServiceKnownTypes? [英] WCF Client having problems recognizing ServiceKnownTypes?

查看:85
本文介绍了WCF客户端无法识别ServiceKnownTypes?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我知道我可以使用[ServiceKnownType]属性,该属性可以使服务调用从WCF测试服务器正常运行,但是仍然无法从客户端运行.我在这里想念东西吗?

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

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

{元素 'http://schemas.datacontract.org/2004/07/BaseClassZ' 包含来自映射到的类型的数据 名字 'http://schemas.datacontract.org/2004/07/SubClassA'. 反序列化器不了解 映射到该名称的任何类型. 考虑使用DataContractResolver 或添加对应于的类型 已知类型列表中的"SubClassA" -例如,通过使用KnownTypeAttribute属性或 将其添加到已知类型列表中 传递给DataContractSerializer.}

使用DataContractSerializer对WCF服务器上的对象进行序列化/反序列化,并可以使用KnownTypes列表.

更新:如果我向基类添加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>

如果要通过代码完成此操作,则需要在服务接口上而不是在操作方法上使用此属性,但我希望使用声明性方法:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}


更新:

我已经建立了一个示例项目,说明了如何使用web.config来配置已知类型首选方法.另一个示例项目展示了第二种方法.


更新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个数据协定在客户端和服务器之间共享,返回集合的方法在生成的客户端代理中未获得正确的已知类型属性.也许这是设计使然.

How would I tell the WCF service what KnownTypes to use when passing data back to the client?

I know I can use the [ServiceKnownType] attribute, which makes the service call run fine from a WCF Test Server, however it still fails from the client. Am I missing something here?

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

Error message from client is:

{"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."}

Serializing/Deserializing the object on the WCF server using a DataContractSerializer and a list of KnownTypes works fine.

UPDATE: It appears I can get the client to read the object correctly if I add KnownType attributes to the base class, but I am still looking for a way around this if possible since the base class is used for a lot of items and I don't want to have to modify the KnownType attributes on the base class anytime I add a new item.

[DataContract]
[KnownType(typeof(SubClassA))]
[KnownType(typeof(SubClassB))]
public class BaseClassZ 
{
    ...
}

解决方案

To avoid deterring your service code put the known types into web.config of the service:

<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>

If you want to do it by code you need to use this attribute on the service interface and not on the operation method but I would prefer the declarative approach:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IFoo
{
    [OperationContract]
    BaseClassZ GetObject();
}


UPDATE:

I've put up a sample project illustrating the use of web.config to configure known types which is my preferred approach. And another sample project demonstrating the second approach.


UPDATE 2:

After looking at your updated code with the Silverlight application client we notice the following definition:

[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);
}

Notice how the BeginGetSingle method contains the known type attributes while the BeginGetMany method doesn't. In fact those attributes should be placed on the service definition so that the class looks like this.

[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);
}

As this is an autogenerated class there could be a bug in the SLsvcUtil.exe and svcutil.exe as it exhibits the same behavior. Putting the known type attributes on their correct place solves the problem. The problem is that this class is autogenerated by a tool and if you try to regenerate it from the WSDL it will mess up again.

So it seems that if you have the following service definition:

[ServiceContract]
[ServiceKnownType(typeof(SubClassA))]
[ServiceKnownType(typeof(SubClassB))]
public interface IService1
{
    [OperationContract]
    BaseClassZ[] GetMany();

    [OperationContract]
    BaseClassZ GetSingle();
}

And the 3 data contracts used here are shared between the client and the server when importing the definition of the service, the method that returns a collection doesn't get the correct known type attributes in the generated client proxy. Maybe this is by design.

这篇关于WCF客户端无法识别ServiceKnownTypes?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆