1 使用场合:
在 WCF Data Contract中如果存在向下造型的情况时你就会用到 KnownTypeAttribute类以保证在反序列化时引擎能知道应该使用哪个具体的类型。主要有以下几种典型的情况:
1 发送的数据契约类型 是从接收端期望接收的数据契约类型继承的。
2 声明的数据类型是接口(注意:集合接口除外,具体请看稍后的 WCF Data Contract之集合类型 )
3 声明的数据类型是 Object.
4 在数据契约中的数据 成员包含前面三种的任何一种时。例如:在 Hashtable内部使用 Object来保存实际对象,所以在接收端并不能确定其中对象的实际类型 ,此时你需要增加 KnownType来告诉反序列化引擎应该使用哪个具体的类型。
[DataContract]
public class ClassA
{
[DataMember]
public string name;
}
[DataContract]
public class ClassB : ClassA
{
[DataMember]
public int department;
[DataMember]
public string title;
[DataMember]
public int salary;
}
Public interface InterfaceA
{
String GetSomething();
}
[DataContract]
Public calss ImplA:InterfaceA
{
Public String GetSomething()
{
Return “don’t know”;
}
}
[DataContract]
Public calss ImplB:InterfaceA
{
Public String GetSomething()
{
Return “don’t know”;
}
}
[DataContract]
Public class ClassC{}
[DataContract]
Public class ClassD{}
[DataContract]
Public class ClassWillProcess
{
[DataMember]
ClassA ca;
[DataMember]
InterfaceA ia;
[DataMember]
ArrayList arraylist1;
[DataMember]
Object numberValue;
}
大家请注意 ClassWillProcessl类型,我们需要增加哪些类型到 KnownType中呢?
1 如果我们在应用中可能将 ClassB的实例赋值给 ca的 话,我们需要增加 ClassB到 KnowType中( [KnowType(typeof(ClassB))] ),因为 ClassB派生于 ClassA,所以在反序列化时存在向下造型。如果不存 在这种可能性的话,可以不加。
2 由于 ia的声明类 型是一个接口,所以我们需要将接口的实现类加到 KnownType中。在 这里是 ImplA和 ImplB。试想一下,如果我们只增加了 ImplA到 KnownType中, 并且我们将 ImplB的实例赋给了 ia,反序列化引擎还是会将其反序列化成 ImplA,因为它只知道 ImplA.
3 如果我们 arraylist1 集合中可能会将 ClassC和 ClassD放入其中,由于非泛型集合都是使用 Object来保存实际对象,所以我们也需要将 ClassC和 ClassD加入 到 KnownType中。
4 如果我们也希望将一个 int的数组存放在 numberValue中(当然在实际情况中很少 发生),我么也需要将 int[]加入到 KnownType中。
增加了 KnownType的 ClassWillProcessl类型如下:
[DataContract]
[KnowType(typeof(ClassB))]
[KnowType(typeof(ImplA))]
[KnowType(typeof(ImplB))]
[KnowType(typeof(ClassC))]
[KnowType(typeof(ClassD))]
[KnowType(typeof(int[]))]
Public class ClassWillProcess
{
[DataMember]
ClassA ca;
[DataMember]
InterfaceA ia;
[DataMember]
ArrayList arraylist1;
Object numberValue;
[DataMember]
Public object Numbers
{
get {return numberValue;}
set {numberValue=value;}
}
}
注:如果对 numberValue赋值时,以下语句都是可以接受的:
ClassWillProcess cwp=new ClassWillProcess();
// 因为 int 是基本类型,对于反序列化引擎来说总是 Known Type 的
int a=10; cwp.Numbers= a;
// 因为 int 数组已经增加到 knownType 中去了
int[] b=new int[100];cwp.Numbers =b;
//List<int> 和 ArrayList 是等价的
List<int> c=new List<int>(); cwp.Numbers=c;
ArrayList d=new ArrayList(); cwp.Numbers=d;
2 使用规则:
2.1 基本类型(如: int,bool)以及被认为是基本类型的某些类型(如: DateTime,XmlElement。 但 DateTimeOffset结构并没有被认为是基本类型)对于反序列化引擎来说总是可知的,不需要通过这种机制来将其加到 KnownType中去。但是基本类型的 Array必须通过这种方式显示的增加,非泛型集合是和 Object的数组是等价的。
2.2 同一类型在同一个命名空间只能用 KnownTypeAttribute应用一次。
2.3 KnownType只能和类和结构进 行关联,不能和接口进行关联。
2.4 KnownType属性是可以继承的。例如:前面 ClassWillProcess 类使用了 KnownType, 如果我们有一个新类派生与 ClassWillProcess 类,我们就不需要在派生类中再添加在 ClassWillProcess 类已经添加了的 KnownType 了 .
2.5 KnownType 的类型参数不能是泛型。但是我 们可以通过定义一个方法并把这个方法名作为 KnownType 参数来解决此问题,但这个方法必须满足以下条件:
a 必须是 static, 因为需要在对象实例化之前调用。
b 必须是不带任何参数的。
C 返回类型必须是可被 IEnumerable 接受的,(也就 是实现了 IEnumerable 接口的)。
同时还必须满足一个类型只能有一个带有方法名参数的 KnownType ,不能再有其他的带有实际类型的 KnownType 应用。如下例 theDrawing 包含 ColorDrawing 和 BlackAndWhiteDrawing 泛型的实例,并且它们都是继承 GenericDrawing 泛型。
[DataContract]
[KnownType("GetKnownType")]
public class DrawingRecord2<T>
{
[DataMember]
private T TheData;
[DataMember]
private GenericDrawing<T> TheDrawing;
private static Type[] GetKnownType()
{
Type[] t = new Type[2];
t[0] = typeof(ColorDrawing<T>);
t[1] = typeof(BlackAndWhiteDrawing<T>);
return t;
}
}
3 其他增加KnownType的方法
3.1 你可以增加类型到ReadOnlyCollection集合中,然后通过 DataContractSerializer的KnownTypes属性来访问。
3.2 也可以通过配置文件的<System.runtime.serialization>节 来增加KnownType,例如:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type = "Contact,Host,Version=1.0.0.0,Culture=neutral,
PublicKeyToken=null">
<knownType type = "Customer,MyClassLibrary,Version=1.0.0.0,
Culture=neutral,PublicKeyToken=null"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
3.3 前面介绍的KnowTypeAttribute是基于DataContract的,我们也可以使用 ServiceKnowTypeAttribute来基于ServiceContract或 OperationContract来设置 KnowType类,例如针对某一个服务操作:
[DataContract]
class Contact
{...}
[DataContract]
class Customer : Contact
{...}
[ServiceContract]
interface IContactManager
{
[OperationContract]
[ServiceKnownType(typeof(Customer))]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
针对整个服务:
[ServiceContract]
[ServiceKnownType(typeof(Customer))]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts( );
}
注意:不管应用 ServiceKnowType是在服务级别还是在操作级别,最后导出到元数据中,都是将KnowType应用在基类中,如上述例子中的导入契约定义为:
[DataContract]
[KnownType(typeof(Customer))]
class Contact
{...}
[DataContract]
class Customer : Contact
{...}
[ServiceContract]
interface IContactManager
{...}