7

我有以下 ServiceContract 和 DataContract 类:

[ServiceContract] 
public interface IWcfService 
{ 
    [OperationContract] 
    Response GetData(); 
} 

[DataContract]
public class Response 
{ 
    [DataMember] 
    public Dictionary<string, object> Data { get; set; } 
} 

当 Response.Data 字典的值是 int、string、double 或任何其他“简单”原始类型时,WCF 可以成功地序列化对象。但是当 Response.Data 字典的值是 List<string> 类型时,客户端在接收到数据并尝试反序列化时抛出以下异常:

Message=The formatter threw an exception while trying to deserialize the message: 
There was an error while trying to deserialize parameter http://tempuri.org/:GetDataResult. 
The InnerException message was 'Error in line 1 position 990. 
    Element 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value' contains data from a type 
    that maps to the name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfstring'. 
    The deserializer has no knowledge of any type that maps to this name. 
    Consider using a DataContractResolver or add the type corresponding to 'ArrayOfstring' 
    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.'.

我还尝试将 KnownType 属性添加到 ServiceContract 和 DataContract,如下所示:

[ServiceContract] 
[ServiceKnownType(typeof(List<string>))] 
[ServiceKnownType(typeof(Dictionary<string, string>))] 
[ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
public interface IWcfService 
{ 
    [OperationContract] 
    [ServiceKnownType(typeof(List<string>))] 
    [ServiceKnownType(typeof(Dictionary<string, string>))] 
    [ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
    Response GetData(); 
} 

[DataContract] 
[ServiceKnownType(typeof(List<string>))] 
[ServiceKnownType(typeof(Dictionary<string, string>))] 
[ServiceKnownType(typeof(Dictionary<string, List<string>>))] 
[KnownType(typeof(List<string>))] 
[KnownType(typeof(Dictionary<string, string>))] 
[KnownType(typeof(Dictionary<string, List<string>>))] 
public class Response 
{ 
    [DataMember] 
    public Dictionary<string, object> Data { get; set; } 
} 

但这些都没有帮助。有人对此有任何想法吗?

更新

数据看起来像:

Data = new new DIctionary<string, object> 
       { 
           {"_id", 12344}, 
           {"names", new List<string>{ "John", "Peter", "Jack"}}, 
           {"time", DateTime.Now} 
       }

我们使用 Dictionary<string, object> 的原因:服务器需要向客户端发送一个“动态”数据的字典,可以是 int、List、DataTime 等。使用 Dictionary 将有助于解决这个问题,但它也会丢失原始类型信息。例如,客户端需要 List 并做一些数据绑定来显示集合,所以 List.ToString() 在这种情况下将没有帮助。

4

3 回答 3

1

我认为默认System.Object情况下不是Serializable。您需要发送额外信息,但这仍然不是最佳实践,我建议在使用您的字典之前定义一个单独的类型。

请参阅以下内容:

WCF 服务返回一个字典数组

WCF System.Object 序列化

在 WCF 中序列化 IDictionary

于 2013-10-21T10:24:18.310 回答
1

请记住,您的数据合约通过网络被序列化为字符串,因此您传递的任何对象都必须是可序列化的(object不是)。

问题是您在面向服务的上下文中使用面向对象的模式 - 期望您在字典值中传递的所有内容都是多态的object。我在这里回答了一个有类似“气味”的问题:https ://stackoverflow.com/a/19445875/2382536 。

问题归因于 WCF 非常擅长抽象出服务的底层通道这一事实,您很容易忘记您现在是在“面向服务”的上下文中工作(KnownTypes是一种 hack - 给人一种 OO 的错觉)电线)。数据契约构成了您服务的“公共”API 的一部分,因此必须是您的服务公开的数据的清晰明确表示。拥有返回“动态”数据的数据结构违反了这一重要规则。

客户端需要知道它返回了什么数据,所以如果你想实现一个“动态”响应,你应该为响应中的变化实现(比如说)不同的方法/端点/服务。在您的服务中,您没有理由不能使用多态性来简化您的代码——但这不应该泄露到公共服务/数据合同中。

于 2013-10-21T11:52:39.217 回答
1

感谢大家的投入。我已经设法通过配置 WCF 使用 NetDataContractSerializer 作为序列化程序(默认是 DataContractSerializer)来解决这个问题。NetDataContractSerializer 在进行序列化时将包含更多 CLR 类型信息,尽管它会影响性能(大约是序列化时间的两倍)。

于 2013-11-25T12:17:02.650 回答