WCF - сериализация унаследованных типов - PullRequest
15 голосов
/ 24 февраля 2010

У меня есть эти классы:

[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.'

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

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

Ответы [ 3 ]

17 голосов
/ 25 февраля 2010

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

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

Подробнее об атрибуте KnownType читайте в этом блоге

7 голосов
/ 25 февраля 2010

Попробуйте это:

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

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

2 голосов
/ 23 октября 2012

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

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

 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. Я не знаю, насколько это надежное решение, но я этим и занимаюсь, и пока оно работает. Это немного медленно, поэтому обязательно кешируйте результат.

...