DataContract KnownType---Data Transfer in Service Contracts
18856 ワード
The KnownTypeAttribute class allows you to specify, in advance, the types that should be included for consideration during deserialization. For a working example, see the Known Types example.
Normally, when passing parameters and return values between a client and a service, both endpoints share all of the data contracts of the data to be transmitted. However, this is not the case in the following circumstances : (all of these have the same point . the receiving endpoint cannot determine in advance the data contract for the transmitted data.)
The sent data contract is derived from the expected data contract. For more information, see the section about inheritance in Data Contract Equivalence ). In that case, the transmitted data does not have the same data contract as expected by the receiving endpoint.
The declared type for the information to be transmitted is an interface, as opposed to a class, structure, or enumeration. Therefore, it cannot be known in advance which type that implements the interface is actually sent and therefore, the receiving endpoint cannot determine in advance the data contract for the transmitted data.
The declared type for the information to be transmitted is Object . Because every type inherits from Object, and it cannot be known in advance which type is actually sent, the receiving endpoint cannot determine in advance the data contract for the transmitted data. This is a special case of the first item: Every data contract derives from the default, a blank data contract that is generated for Object.
Some types, which include .NET Framework types, have members that are in one of the preceding three categories. For example, Hashtable uses Object to store the actual objects in the hash table. When serializing these types, the receiving side cannot determine in advance the data contract for these members.
The following examples show the KnownTypeAttribute class in use.
In the following example, even though both
In the following example, a Hashtable stores its contents internally as Object. To successfully deserialize a hash table, the deserialization engine must know the set of possible types that can occur there. In this case, we know in advance that only
In the following example, a data contract stores a number and an operation to perform on the number. The
This is the application code.
When a known type is associated with a particular type using the KnownTypeAttribute attribute, the known type is also associated with all of the derived types of that type. For example, see the following code.
It may be necessary to add a generic type as a known type. However, an open generic type cannot be passed as a parameter to the KnownTypeAttribute attribute.
This problem can be solved by using an alternative mechanism: Write a method that returns a list of types to add to the known types collection. The name of the method is then specified as a string argument to the KnownTypeAttributeattribute due to some restrictions.
The method must exist on the type to which the KnownTypeAttribute attribute is applied, must be static, must accept no parameters, and must return an object that can be assigned to IEnumerable of Type .
You cannot combine the KnownTypeAttribute attribute with a method name and KnownTypeAttribute attributes with actual types on the same type. Furthermore, you cannot apply more than one KnownTypeAttribute with a method name to the same type.
You can also add types to the ReadOnlyCollection , accessed through the KnownTypes property of the DataContractSerializer .
Additionally, known types can be added through a configuration file. This is useful when you do not control the type that requires known types for proper deserialization, such as when using third-party type libraries with Windows Communication Foundation (WCF).
The following configuration file shows how to specify a known type in a configuration file.
In the preceding configuration file a data contract type called
Normally, when passing parameters and return values between a client and a service, both endpoints share all of the data contracts of the data to be transmitted. However, this is not the case in the following circumstances : (all of these have the same point . the receiving endpoint cannot determine in advance the data contract for the transmitted data.)
The sent data contract is derived from the expected data contract. For more information, see the section about inheritance in Data Contract Equivalence ). In that case, the transmitted data does not have the same data contract as expected by the receiving endpoint.
The declared type for the information to be transmitted is an interface, as opposed to a class, structure, or enumeration. Therefore, it cannot be known in advance which type that implements the interface is actually sent and therefore, the receiving endpoint cannot determine in advance the data contract for the transmitted data.
The declared type for the information to be transmitted is Object . Because every type inherits from Object, and it cannot be known in advance which type is actually sent, the receiving endpoint cannot determine in advance the data contract for the transmitted data. This is a special case of the first item: Every data contract derives from the default, a blank data contract that is generated for Object.
Some types, which include .NET Framework types, have members that are in one of the preceding three categories. For example, Hashtable uses Object to store the actual objects in the hash table. When serializing these types, the receiving side cannot determine in advance the data contract for these members.
The following examples show the KnownTypeAttribute class in use.
[DataContract]
public class Shape { }
[DataContract(Name = "Circle")]
public class CircleType : Shape { }
[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
[DataMember]
private Shape ShapeOfLogo;
[DataMember]
private int ColorOfLogo;
}
In the following example, even though both
CustomerTypeA
and CustomerTypeB
have the Customer
data contract, an instance of CustomerTypeB
is created whenever a PurchaseOrder
is deserialized, because only CustomerTypeB
is known to the deserialization engine. public interface ICustomerInfo
{
string ReturnCustomerName();
}
[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
[DataMember]
ICustomerInfo buyer;
[DataMember]
int amount;
}
In the following example, a Hashtable stores its contents internally as Object. To successfully deserialize a hash table, the deserialization engine must know the set of possible types that can occur there. In this case, we know in advance that only
Book
and Magazine
objects are stored in the Catalog
, so those are added using the KnownTypeAttribute attribute. [DataContract]
public class Book { }
[DataContract]
public class Magazine { }
[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
[DataMember]
System.Collections.Hashtable theCatalog;
}
In the following example, a data contract stores a number and an operation to perform on the number. The
Numbers
data member can be an integer, an array of integers, or a List that contains integers. [DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
private object numberValue;
[DataMember]
public object Numbers
{
get { return numberValue; }
set { numberValue = value; }
}
//[DataMember]
//public Operation Operation;
}
This is the application code.
static void Run()
{
MathOperationData md = new MathOperationData();
// This will serialize and deserialize successfully because primitive
// types like int are always known.
int a = 100;
md.Numbers = a;
// This will serialize and deserialize successfully because the array of
// integers was added to known types.
int[] b = new int[100];
md.Numbers = b;
// This will serialize and deserialize successfully because the generic
// List<int> is equivalent to int[], which was added to known types.
List<int> c = new List<int>();
md.Numbers = c;
// This will serialize but will not deserialize successfully because
// ArrayList is a non-generic collection, which is equivalent to
// an array of type object. To make it succeed, object[]
// must be added to the known types.
ArrayList d = new ArrayList();
md.Numbers = d;
}
When a known type is associated with a particular type using the KnownTypeAttribute attribute, the known type is also associated with all of the derived types of that type. For example, see the following code.
[DataContract]
[KnownType(typeof(Square))]
[KnownType(typeof(Circle))]
public class MyDrawing
{
[DataMember]
private object Shape;
[DataMember]
private int Color;
}
[DataContract]
public class DoubleDrawing : MyDrawing
{
[DataMember]
private object additionalShape;
}
It may be necessary to add a generic type as a known type. However, an open generic type cannot be passed as a parameter to the KnownTypeAttribute attribute.
This problem can be solved by using an alternative mechanism: Write a method that returns a list of types to add to the known types collection. The name of the method is then specified as a string argument to the KnownTypeAttributeattribute due to some restrictions.
The method must exist on the type to which the KnownTypeAttribute attribute is applied, must be static, must accept no parameters, and must return an object that can be assigned to IEnumerable of Type .
You cannot combine the KnownTypeAttribute attribute with a method name and KnownTypeAttribute attributes with actual types on the same type. Furthermore, you cannot apply more than one KnownTypeAttribute with a method name to the same type.
[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;
}
}
You can also add types to the ReadOnlyCollection , accessed through the KnownTypes property of the DataContractSerializer .
Additionally, known types can be added through a configuration file. This is useful when you do not control the type that requires known types for proper deserialization, such as when using third-party type libraries with Windows Communication Foundation (WCF).
The following configuration file shows how to specify a known type in a configuration file.
<configuration>
<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"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
</configuration>
In the preceding configuration file a data contract type called
MyCompany.Library.Shape
is declared to have MyCompany.Library.Circle
as a known type.