WCF 中的序列化是用DataContractSerializer,所有被[DataContract]和[DataMemeber]标记的类和属性会被DataContractSerializer序列化。在WCF中使用Contract模式来分辨和指定序列化/反序列化的类型,它是通过http://xmlns/Class这样的命名空间来标识这个序列化的对象的,一旦在序列化过程中无法找到这样的标识(比如某个字段,或者子对象)时,序列化就会失败。KnownTypeAttribute就提供了为我们通知序列化器去寻找未知对象的映射的途径。在Remoting中这样的问题不会存在,因为Remoting实际上是通过将一个类型传递给双方来进行类型匹配的。

那么KnowTypeAttribute到底用在什么地方呢?上边说了,当前类的未知类型。那什么又是当前类的未知类型呢?或许说未知类型有些不恰当,但下边的实际应用可能会让你更清楚这到底是指什么。

1). 序列化对象是从期望返回的类型继承;

2). 无法确定当前所使用类型的。例如Object类型,或者接口类型,你需要告诉序列化器去寻找确切的类来进行序列化。

3). 使用泛型类型作为期望返回类型的;

4). 使用像ArrayList等以object为基础类型存储对象的;

下边我们以第一种类型为例说明KnownTypeAttribute的用法。序列化对象一般是参与到在服务端和客户端传递的数据。在面向对象的设计中,继承可以很好的解决很多业务问题,并简化处理。而在下边的例子中我们可以看出KnownType到底能够做什么。

namespace WcfServiceDemo

{

[DataContract]

public class Address

{

public Address()

{

……

}

[DataMember]

public string AddressCategory { get; set; }

[DataMember]

public string AddressTitle { get; set; }

[DataMember]

public string AddressDetail { get; set; }

}

[DataContract]

public class BusinessAddress : Address

{

……

}

[DataContract]

public class HomeAddress : Address

{

……

}

}

public class Service1 : IService1

{

public Address GetAddress(bool isHome)

{

if (isHome)

return new HomeAddress();

else

return new BusinessAddress();

}

}

上边的代码中简单的生命了三个数据契约类型,Address作为基类,HomeAddress和BusinessAddress继承于Address类。在Service实现中,简单的通过一个参数来判断到底返回哪个子类。我们可以简单的写个客户端来调用一下这个服务,但很快你会发现浏览器(客户端)返回给你了错误:

Server Error in '/' Application.


The underlying connection was closed: The connection was closed unexpectedly.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Net.WebException: The underlying connection was closed: The connection was closed unexpectedly.

很显然,这个错误并不能清楚的反应具体发生了什么,如果你跟进服务你会发现,所有的操作都正常,只是在返回数据的时候发生了错误。如果你愿意,你可以用DataContractSerializer类的一些方法来将一个HomeAddress序列化并反序列化为一个Address类型,你就会看到这个过程中发生了错误,序列化都无法进行。为什么呢?原因是HomeAddress/BusinessAddress的类型标识(如http://www.demo.com/BusinessAddress)无法序列化Address类型时匹配,它就不知道该把它序列化成哪个确切的类型。

解决方法,给Address添加KnownTypeAttribute标识,当一个HomeAddress对象或者BusinessAddress对象被传递到Address进行序列化时,序列化器认识这个对象并能根据契约来进行序列化。

[DataContract]

[KnownType(typeof(BusinessAddress))]

[KnownType(typeof(HomeAddress))]

public class Address

{

……

}

再次调用客户端(注意:如果你是通过Service Reference来引用服务的,那你必须在编译完服务端后选择Update Service Reference来更新服务引用,否则你的变化不会反应到客户端调用),现在你应该可以看到结果了。对于KnownTypeAttribute它还有一个可以替换的选择ServiceKnownTypeAttribute,你可以将它应用到一个Service或者一个Operation(他们的区别是:当把ServiceKnownType标记给以个Service,那么在这个Service内KnownType都发生作用;而对于Operation则只在这个Operation时有效,即作用域不同。)。以下代码同样解决了上述问题:

[OperationContract]

[ServiceKnownType(typeof(BusinessAddress))]

[ServiceKnownType(typeof(HomeAddress))]

Address GetAddress(bool isHome);

KnownType的另外一种标识方式是不用加代码的,在配置文件中通过声明型编程实现:

<system.runtime.serialization>

<dataContractSerializer>

<declaredTypes>

<add type="WcfServiceDemo.Address, MyAssembly, Version=2.0.0.0, Culture=neutral,PublicKeyToken=null, processorArchitecture=MSIL">

<knownType type="MyCompany.Library.Circle, MyAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL"/>

</add>

</declaredTypes>

</dataContractSerializer>

</system.runtime.serialization>

而对于泛型来说,KnownType是不能被直接应用的,如果你写成[KnownType(typeof(BusinessAddress<>))]这样是不允许,但CLR给我们提供了另外一种方法来实现对序列化的通知:

[KnownType("GetKnownTypes")]

public class Address<T>

{

static Type[] GetKnownTypes()

{

return new Type[] { typeof(HomeAddress<T>) ,typeof(BusinessAddress)};

}

}

我们指定寻找KnownType时到GetKnownTypes方法去找,GetKnownTypes方法返回一个告知序列化器映射类型的数组。既然我们可以用方法,那是否意味着我们也可以动态的来定义GetKnownTypes返回的类型集合呢?当然,在上边的例子中你可以通过扩展GetKnownTypes方法来实现。但为了DataContract类型定义的简约和独立性,我们不妨将这个函数摘出来,或许更有利于程序结构:

[ServiceContract]

[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]

public interface IService1

{

……

}

static class KnownTypesProvider

{

static Type[] GetKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)

{

Type contractType = (Type)knownTypeAttributeTarget;

return contractType.GetGenericArguments() ;

}

}

我们将类型通知提供者定义到函数外部,并通过利用ServiceKnownType的重载构造函数传入需要去寻找的通知类提供者及其方法。这样你很容易扩展这个函数并动态加载通知类型。

你仍然可以通过使用配置文件的方式来达到同样的功能:

  <system.runtime.serialization>

<dataContractSerializer>

<declaredTypes>

<add type="MyCompany.Library.Shape,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL">

<knownType type="MyCompany.Library.Circle,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL">

<parameter type="System.Collections.Generic.Dictionary">

<parameter type="System.String"/>

<parameter index="0"/>

</parameter>

</knownType>

</add>

</declaredTypes>

</dataContractSerializer>

</system.runtime.serialization>

以上代码就指定了将Circle<Dictionary<string, T>>作为Shape<T>的一个knownType。注意,当你指定某个knownType可能用到多于一个参数为泛型对象的参数时,通过index来指定与期望类型T不同的另外一个类型。例如上边的代码中指定了第一个参数为String类型(通过index指定),第二个参数T与Shape<T>的T相同,不用特别指定。

再说WCF Data Contract KnownTypeAttribute的更多相关文章

  1. 谈谈WCF中的Data Contract(3):WCF Data Contract对Collection & Dictionary的支持

    谈谈WCF中的Data Contract(3):WCF Data Contract对Collection & Dictionary的支持 在本篇文章上一部分Order Processing的例 ...

  2. XML序列化与REST WCF Data Contract匹配时遇到的2个问题

    问题一: XML序列化与RESTful WCF Data Contract不能匹配,无法传递类的值. 现象: 给类加上[Serializable]Attribute,可以成功序列化,但是WCF Ser ...

  3. WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)

    原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济 ...

  4. Type 'System.IO.FileStream' with data contract name 'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not expected.

    今天在WCF项目里使用DataContract序列化接口参数的时候,报了这个错,错误详细信息如下: System.ServiceModel.CommunicationException: There ...

  5. WCF Data Service

    WCF Data Service:http://www.cnblogs.com/shanyou/category/240225.html

  6. 我的WCF Data Service 系列 (一、为什么要有WCF Data Service)

    开篇先说两名题外话, 在博问上,经常看到有个问性能问题,比如Entity Framework的性能行不行啊之类的. 其实这个行不行,关键还是看对象,一夜家族的老七可能勉强吃点蓝片片,也就行了,可真要让 ...

  7. 精进不休 .NET 4.5 (12) - ADO.NET Entity Framework 6.0 新特性, WCF Data Services 5.6 新特性

    [索引页][源码下载] 精进不休 .NET 4.5 (12) - ADO.NET Entity Framework 6.0 新特性, WCF Data Services 5.6 新特性 作者:weba ...

  8. WCF Data Service 使用小结(二) —— 使用WCF Data Service 创建OData服务

    在 上一章 中,介绍了如何通过 OData 协议来访问 OData 服务提供的资源.下面来介绍如何创建一个 OData 服务.在这篇文章中,主要说明在.NET的环境下,如何使用 WCF Data Se ...

  9. WCF Data Service 使用小结 (一)—— 了解OData协议

    最近做了一个小项目,其中用到了 WCF Data Service,之前是叫 ADO.NET Data Service 的.关于WCF Data Service,博客园里的介绍并不多,但它确实是个很好的 ...

随机推荐

  1. C#TreeView读取Xml,TreeView导出到Xml

    实现功能有1.根据Xml生成TreeView2.双击修改节点3.右键添加子节点或添加要节点4.右键删除当前选择的节点5.将修改后的TreeView重新生成Xml文档 其实这个主要是实现 了Xml生成T ...

  2. Python Challenge 第九关

    第九关只有一幅图,上面有一些黑点.网页名字叫:connect the dots.可能是要把这些点连起来. 查看源代码,果然有两个整数集合 first 和 second.并且有个提示:first+sec ...

  3. JS-严格模式、非严格模式

    2018年11月14日晚上,我在“深入理解javascript”书上第一次知道“严格模式”“非严格模式”这2个名词: “严格模式”使用指令:“use strict”: 这个指令我其实有经常看到,在其他 ...

  4. js坑 把数字型的字符串默认为数字 把前面的0给去掉了("001")

    <script> b("); function b(id) { console.log("b函数的id:"+id); //var history = &quo ...

  5. encodeURI 解码 编码

    var uriStr = "http://www.baidu.com?name=张三&num=001 zs"; var uriec = encodeURI(uriStr); ...

  6. 最新Webstrom, Idea 2019.1.3 的激活

    1.注册码激活 打开网址(IntelliJ IDEA 注册码),我们能看到下面的界面,直接点击获取激活码,将生成的激活码粘贴到WebStorm激活对话框中的Lisence Code输入框,点击OK即可 ...

  7. 关于Web应用和容器的指纹收集以及自动化软件的制作

    一次对Web应用的渗透,九成都是从信息收集开始,所以信息收集就显得尤为重要.关键信息的收集可以使你在后期渗透的时候更加的得心应手,把渗透比喻成走黑暗迷宫的话,那信息收集可以帮你点亮迷宫的大部分地图. ...

  8. 修改Linux基本配置

    1.修改主机名 vi /etc/sysconfig/network NETWORKING=yes HOSTNAME=server1.cn 2.修改ip地址 vi /etc/sysconfig/netw ...

  9. RMQ(区间求最值)

    1. 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A.回答若干询问RMQ(A,i,j)(i,j<=n).返回数列A ...

  10. Linux下快速安装Mysql及使用

    1.安装 查看有没有安装过: yum list installed mysql* rpm -qa | grep mysql* 查看有没有安装包: yum list mysql* 安装mysql客户端: ...