When working with Web Services it is often the case that service utility "svcutil" generates classes which contains attribute KnownTypeAttribute. Because people very often asking about meaning of this attribute, I will give very short example which briefly describes its purpose. Assume there is a service IService, which has two operations as shown below:
[System.ServiceModel.ServiceContract] public interface IService { [OperationContract] BusinessObjectBase GetProcess(string businessObject);
[OperationContract] BusinessObject1 GetProcess1(string businessObject); }
|
[DataContract(Namespace = HcmService.cNamespace)] public class BusinessObjectBase { [DataMember] public Guid Id { get; set; }
[DataMember] public string Key { get; set; } }
[DataContract(Namespace = HcmService.cNamespace)] public class BusinessObject1 { [DataMember] public string Key1 { get; set; } }
|
Note that BusinesObjectBase represents a bas class and BusinesObject1 derives from the base class. Also note, that it is not important what these two methods will do with business objects. Important is, that there is at least one operation which use both business objects. In other words, as long there is no operation which uses one object, the object will NOT BE a part of service contracts. For example, if operation GetProcess1 wouldn't exists, the contract would include BusinessObjectBase type only.
If you start svcutil against this service as shown above, the imported contract will import both types. Now, let's take a look on imported types.
[SerializableAttribute()] [KnownTypeAttribute(typeof(BusinessObject1))] public partial class BusinessObjectBase : object, IExtensibleDataObject, INotifyPropertyChanged { . . .}
[SerializableAttribute()] public partial class BusinessObject1 : object, IExtensibleDataObject, { . . .}
|
As you see svcutil has disovered that BusinessObject1 is derived from BusinessObjectBase. It means BusinessObjectBase has attribute KnownType helps serializer to deserialize BusinessObject1 if service returned it instead of BusinessObjectBase. Now it is interesting, that this is not a service setting. It is all about intelligence of svcutil. So, svcutil will alwas try to discover right types and append them as KnownAttribute. This allows serializer at the client side to work properly.
Unfortunately this is one part of the story only, because the example shown above will not work. Imagine the service implementation of GetProcess looks like:
public BusinessObjectBase GetProcess(string businessObject)
{
return new BusinessObject2();
}
You may not believe, but this code will fail at the service side with following exception, shic can only be seen in the WCF trace viewer.
There was an error while trying to serialize parameter http://tempuri.org/:GetProcessResult. The InnerException message was 'Type BusinessObject2' with data contract name 'BusinessObject2:' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
When trying to invoke the service you will get following exception at the client side:
{"The underlying connection was closed: The connection was closed unexpectedly."}
To solve the problem you have to decorate the BusineObjectBase class at the service side as shown:
[KnownType(typeof(BusinessObject1))] [KnownType(typeof(BusinessObject2))] [DataContract(Namespace = HcmService.cNamespace)] public class BusinessObjectBase { [DataMember] public Guid Id { get; set; }
[DataMember] public string Key { get; set; } }
|
This instructs serializer at the service side that when serializing return values or deserializing requests instead of BusinessObjectBase two more types can be used. Now you can return in GetProcess both types BusinessObjec1 and BusinessObjec2.
One more thing. In examples above without of last decoration at service side, svcutil has knowledge about BusinessObject11, because it is used as part of contract of operation GetProcess2. This is a reason why svcutil has decorated BusinessObjectBase with KnownType=BusinessObject1. Because BusinessObject2 is not part of contract in any of operations, the client will not known anything about it. If client doesn't know about type, but service knows (this happens when client has imported reference to service and service has implemented new type later.) following exception will be thrown at the client side, when service returns a new type:
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:GetProcessResult. The InnerException message was 'Error in line 1 position 471. Element 'http://tempuri.org/:GetProcessResult' contains data of the 'BusinessObject2' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'BusinessObject2' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
Sometimes it is useful to have a method which dynamically creates known types. That is when your types are possibly dynamically created. In such cases it is not possible to declare known types if types are not know at design time. Follwong example shows how to do this by using of ServiceKnownType instead of KnownType.
ServiceKnownType("GetAllMyKnownTypes", typeof(KnownTypeContainer))] [ServiceContract()] public interface ITableService { // Table could contain any type [OperationContract] Hashtable GetTableItems(); }
static class KnownTypeContainer { public static IEnumerable<Type> GetAllMyKnownTypes(ICustomAttributeProvider p) { System.Collections.Generic.List<System.Type> dynamicyTypes = new System.Collections.Generic.List<System.Type>();
// Here I'm adding my static types. // Usually I could generate the type on the fly and append it here. dynamicyTypes.Add(typeof(MyType1)); dynamicyTypes.Add(typeof(ByType2)); return dynamicyTypes; } }
|
:)
Posted
Mar 24 2009, 10:26 PM
by
Damir Dobric