WCF Data Contract之KnownType
WCF Data Contract之KnownType
LazyBee
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
{...}