WCF - 序列化继承类型

15

我有这些类:

[DataContract]
public class ErrorBase {}

[DataContract]
public class FileMissingError: ErrorBase {}

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

我的服务方法返回的是类ResponseFileInquiry的实例。如果我使用ErrorBase的实例填充ResponseFileInquiry.errors,一切都能正常工作。但是,如果我添加一个继承类型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

你应该在基类中添加KnownType属性。

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

阅读此博客,了解更多关于KnownType属性的信息。


1
谢谢,博客文章中列出了所有已知类型声明的可能方式。 - Andrey
在我的情况下,使用KnownType并不能帮助太多,因为类型来自我没有引用的单独程序集。其他开发人员正在扩展我为DataContract拥有的类以添加一些属性。如果我只想丢弃任何派生类并使用基类怎么办? - brendonparker
这是我不得不使用的解决方案:http://stackoverflow.com/a/8414390/2460073 注意:我必须将客户DataContractSerializer应用于ClientBase。 - brendonparker

7

试试这个:

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

正如错误信息所述,任何无法静态知道的信息(例如您在此处表达的多态关系)必须通过属性提供。 在这种情况下,您需要指定您的FileMissingError数据契约是其基类ErrorBase的已知类型。

那么这意味着我需要在这里指定所有子错误类吗? 还有其他的方法吗?我不喜欢父类以属性和“using”子句的方式知道子类。异常说:“将任何静态未知的类型添加到已知类型列表中-例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。” 那么有没有办法只是将它们添加到已知类型列表中?我该怎么做? - Andrey
2
只有在手动序列化合同时,您才能将已知类型列表传递给序列化。由于看起来您正在允许WCF为您处理序列化,因此您唯一能做的就是向基类添加一个KnownTypeAttribute,每个子类都需要知道一个。 - Andrew Hare

2
略晚了一点,但或许对未来的读者有帮助。=)
如果你不想在父类中为每个子类添加属性,你可以在父类的静态构造函数中使用列表构造已知类型的列表。
 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构造函数。我不知道这种解决方案的稳健性如何,但这就是我正在做的,到目前为止它可以工作。它有点慢,所以请确保缓存结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接