I am learning WCF and do not understand the real advantage of KnowTypeAttribute. Could somebody explain me simply why we need it?
DataContractSerializer is contract-based, meaning it is not tied to any specific type model. All it has is data (typically xml). This means that if you have a model like:
Customer
SuperCustomer : Customer
AwesomeCustomer : Customer
then the serializer needs to know in advance what each type means if it sees it in the data; otherwise it won't know what type to create. This is done in a number of ways, the simplest of which is KnownTypeAttribute.
Consider the alternative; all the serializer knows is "Customer", which it expects to see as <customer>...</customer>
in some xml. Instead, it gets something else (it doesn't matter what, but let's say <superCustomer>...</superCustomer>
. Now what does it do? Does it start scrounging for likely looking types? that is very imprecise and risky. Also consider, it needs to be able to generate a WSDL/MEX export for this data - if all it knows about is "Customer", it can't possible warn callers to also expect SuperCustomer / AwesomeCustomer - which would mean the WSDL/MEX is incomplete and useless.
This same approach is used by XmlSerializer (XmlIncludeAttribute) and protobuf-net (ProtoIncludeAttribute), and probably my most contract based serializers.
The alternative is type based serializers (BinaryFormatter, NetDataContractSerializer, etc) - in this it includes the type in the data, meaning Your.Namespace.Type, Your.Assembly, blah
- this means it doesn't need to know in advance (since it is explicit in the data), but also means that it can't possibly work for different models (or indeed, cross-platform).
KnownTypeAttribute enable you to designate acceptable derived class for a given Data Contract. It specifies types that should be recognized by the DataContractSerializer when serializing or deserializing a given type.
One simple example.
[ServiceContract()]
interface ITaskManager
{
[OperationContract()]
MyCollection<Task> GetTaskByAssignedName(string name);
}
[DataContract()]
[KnownType(typeof(DerivedTask))]
class Task
{
}
[DataContract()]
class DerivedTask
{
}
When working with polymorphic types in your service contract, the KnownTypeAttribute is required because polymorphism is outside the paradigm of the service orientation.
Apply the KnownTypeAttribute attribute to a type to indicate to the DataContractSerializer types that should be recognized when serializing or deserializing an instance of the type to which the attribute is applied. This attribute could also be recognized by other serializers that understand data contracts.
Look at here for more details.
This attribute is used to include additional classes in the metadata of the service so that clients could see them. Let's take for example the following:
[DataContract]
public class BaseModel
{
[DataMember]
public string Id { get; set; }
}
[DataContract]
public class ChildModel: BaseModel
{
[DataMember]
public string Foo { get; set; }
}
and the following service contract:
[ServiceContract]
public interface IMyService
{
[OperationContract]
BaseModel Get();
}
and that you implement it like this:
public class MyService: IMyService
{
public BaseModel Get()
{
return new ChildModel();
}
}
Now when WCF exposes the metadata of this service it looks at the service contract and the operations being involved so it discovers the Get operation which returns the BaseModel type. So the BaseModel class is automatically exposed in the metadata. The problem is that when you try to invoke the service the actual implementation returns a ChildModel
for WCF has no knowledge. The clients of the service neither have knowledge of this type.
So you need to explicitly indicate indicate this class that you are using in the implementation but is not part of the contract. This could be done by using the KnownType attribute:
[DataContract]
[KnownType(typeof(ChildModel))]
public class BaseModel
{
[DataMember]
public string Id { get; set; }
}
Another way to specify this known type is to do it by using the config file:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyCompany.BaseModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
<knownType type="MyCompany.ChildModel, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With