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. 线程之间通信 等待(wait)和通知(notify)

    线程通信概念: 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程之间的通信就成为整体的必用方式之一.当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同 ...

  2. Centos7 安装docker ce

    一. 安装docker 1.升级rpm包 yum -y update 2. 通过命令设置Docker CE 资源库: yum install -y yum-utils yum-config-manag ...

  3. Linux和Cisco命令行通用快捷键。

    Ctrl a e 行首,行尾(ahead,end)Esc f b 单词首,单词尾Ctrl f b 移动光标(forward,backwards) Ctrl u k 剪切光标前所有,剪切光标后所有Ctr ...

  4. (21)python lambda表达式

    lambda表达式是一个匿名函数 通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用 最简单的例子 add = lambda x,y : x + y print add(3,5) #out ...

  5. 51nod 1050 循环数组最大子段和【环形DP/最大子段和/正难则反】

    1050 循环数组最大子段和 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  关注 N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该 ...

  6. Educational Codeforces Round 34 A. Hungry Student Problem【枚举】

    A. Hungry Student Problem time limit per test 1 second memory limit per test 256 megabytes input sta ...

  7. UOJ 外星人

    题目: 2044年,Picks建成了人类第一台基于量子理论的银河系信息传递机.Picks游遍了宇宙,雇用了n个外星人来帮他作为信息传递机的中转站.我们将外星人依次编号为1 到n,其中i 号外星人有ai ...

  8. Maven在[INFO] Generating project in Interactive mode卡住的问题解决

    我的环境: Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven ...

  9. UIView的任意圆角

    今天在做项目的时候,遇到一个问题,grouped类型的tableview 怎么样才能让他们的一个view 其中一个角圆角? 如上图所示,其实我是用UILabel,但是箭头的位置总是尖的不太好看.设置l ...

  10. xamarin.from ToolbarItem 字体大小和颜色更改

    在xamarin.from 上我们经常会使用到页面跳转方式, new NavigationPage(newp page()){ BarBackgroundColor=Color.FromHex(&qu ...