WCF - probem с сериализацией унаследованных типов

У меня есть следующие классы:

[DataContract]
public class ErrorBase {}

[DataContract]
public class FileMissingError: ErrorBase {}

[DataContract]
public class ResponseFileInquiry
{
  [DataMember]
  public List<ErrorBase> errors {get;set;};
}

Экземпляр класса ResponseFileInquiry - это то, что мой метод сервиса возвращается клиенту. Теперь, если я заполню ResponseFileInquiry.errors с экземплярами ErrorBase, все работает нормально, но если я добавлю экземпляр унаследованного типа FileMissingError, во время сериализации я получаю исключение со стороны службы:

Type 'MyNamespace.FileMissingError' with data contract name 'FileMissingError' 
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.'

Таким образом, сериализатор запутывается, потому что ожидает, что List будет содержать объявленные объекты типа (ErrorBase), но получит объекты с унаследованным типом (FileMissingError).

У меня есть целая группа типов ошибок, и Список будет содержать их комбинации, поэтому что я могу сделать, чтобы заставить его работать?

Ответы

Ответ 1

Вы должны добавить атрибут KnownType в базовый класс

[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}

Подробнее об атрибуте KnownType в этом blog

Ответ 2

Попробуйте следующее:

[DataContract]
[KnownType(typeof(FileMissingError))]
public class ErrorBase {}

Как указано в сообщении об ошибке, любая информация, которая не может быть известна статически (например, полиморфные отношения, которые вы здесь изложили), должна предоставляться через атрибуты. В этом случае вам нужно указать, что ваш контракт на FileMissingError - это известный тип его базового класса, ErrorBase.

Ответ 3

Немного поздно, но, возможно, для будущих поколений. =)

Если вы не хотите добавлять атрибут для каждого дочернего класса к вашему родительскому классу, вы можете создать список известных типов в статическом конструкторе родительских классов, используя

 IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain
                                             .GetAssemblies()
                                             .Where(a => !a.GlobalAssemblyCache);

 IEnumerable<Type> serializableTypes = assemblies.SelectMany(a => a.GetTypes())
                                                 .Where(t => IsSerializable(t));

// ...

private static bool IsSerializable(Type type)
{
    return type.GetCustomAttributes(true).Any(a => a is DataContractAttribute);
}

и передать этот список конструктору de/serializers. Я не знаю, насколько это надежное решение, но то, что я делаю, и пока оно работает. Он немного медленный, поэтому убедитесь, что вы кешировали результат.