Sunday, April 12, 2009

WCF, Known Types, Serialization, Dictionary

Если вам знакомо большинство слов из названия статьи то вам сюда.

Итак, проблема. Есть контракт сервиса:


  1. [ServiceContract]

  2. public interface IService1

  3. {

  4. [OperationContract]

  5. string GetData(int value);

  6. [OperationContract]

  7. CompositeType GetDataUsingDataContract(CompositeType composite);

  8. }


* This source code was highlighted with Source Code Highlighter.
Описание CompositeType ниже:


  1. [DataContract]

  2. public class CompositeType

  3. {

  4. [DataMember]

  5. public string StringValue

  6. {

  7. get;

  8. set;

  9. }

  10. [DataMember]

  11. public Dictionary<string, object> Parameters

  12. {

  13. get;

  14. set;

  15. }

  16. }


* This source code was highlighted with Source Code Highlighter.
Все довольно просто.
Создаем простой клиент для этого сервиса и пытаемся передать int[] в нашем словаре параметров CompositeType.Parametes (Dictionary):


  1. Service1Client client = new Service1Client();

  2. CompositeType input = new CompositeType();

  3. input.Parameters= new Dictionary<string, object>();

  4. input.Parameters.Add("array", new int[] { 1, 2 });

  5. CompositeType output = client.GetDataUsingDataContract(input);


* This source code was highlighted with Source Code Highlighter.
И при работе получаем такое вот исключение:
There was an error while trying to serialize parameter http://tempuri.org/:composite. The InnerException message was 'Type 'System.Int32[]' with data contract name 'ArrayOfint:http://schemas.microsoft.com/2003/10/Serialization/Arrays' 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.

В интернете куча таких постов, которые до сих пор висят без ответа. Ни ServiceKnownType, ни KnowType аттрибуты не помогают. Потому что они работают для классов-наследников.

Решение этой проблемы гениально просто.
Добавляем аттрибут [KnownType(typeof(int[]))] или [ServiceKnownType(typeof(int[]))] куда нужно и в DataContract-класс добавляем поле типа System.Object.
(For english readers: solution is pretty smart. Just add to DataContract-class DataMember field of type System.Object. Then use KnownType or ServiceKnownType attributes )


  1. [DataContract]

  2. [KnownType(typeof(int[]))]

  3. public class CompositeType

  4. {

  5. [DataMember]

  6. public object UsedForKnownTypeSerializationObject;

  7. [DataMember]

  8. public string StringValue

  9. {

  10. get;

  11. set;

  12. }

  13. [DataMember]

  14. public Dictionary<string, object> Parameters

  15. {

  16. get;

  17. set;

  18. }

  19. }


* This source code was highlighted with Source Code Highlighter.

Похожий вопрос тута
All about known types
Data Contract Known Types

No comments: