WebService中ICollection属性引发(400) Bad Request错误的原因及解决方法
异常产生原因
WCF 默认使用 DataContractSerializer 进行数据序列化与反序列化,你遇到的400错误核心原因如下:
ClassA中的Children属性是接口类型ICollection<ClassA>,序列化器反序列化时无法直接实例化接口,找不到对应可构造的具体实现类型,就会抛出序列化失败的异常,最终表现为400 Bad Request。ClassB中的Children属性是具体实现类List<ClassB>,序列化器可以直接构造该类型的实例,所以Test2方法可以正常运行。
如果你的 virtual 关键字是为了适配Entity Framework的懒加载导航属性,还要额外注意序列化时如果DbContext未释放,懒加载触发可能引发循环引用序列化异常,也是常见的400错误诱因。
解决方案(保留ICollection类型不变)
以下方案任选其一即可:
方案1:给属性指定序列化已知类型
在数据类或者服务契约上标注 KnownType/ServiceKnownType,告诉序列化器遇到ICollection<ClassA>时使用的具体实现类型:
// 给ClassA标注已知类型 [KnownType(typeof(List<ClassA>))] public class ClassA { public int Id { get; set; } public virtual ICollection<ClassA> Children { get; set; } }
如果有多个ICollection的实现类会用到,可以同时添加多个KnownType标注。
方案2:给属性初始化默认实现
直接在属性定义时初始化具体实现类的实例,序列化器会自动识别实际使用的类型:
public class ClassA { public int Id { get; set; } // 保留ICollection声明,同时初始化默认List实例 public virtual ICollection<ClassA> Children { get; set; } = new List<ClassA>(); }
该方案同时可以避免空引用问题,是最推荐的处理方式。
额外适配循环引用(如果存在导航属性循环引用)
如果你的业务场景中存在ClassA的Children嵌套关联,需要额外开启序列化引用跟踪:
[DataContract(IsReference = true)] public class ClassA { [DataMember] public int Id { get; set; } [DataMember] public virtual ICollection<ClassA> Children { get; set; } = new List<ClassA>(); }
内容的提问来源于stack exchange,提问作者AndrejK
相关产品推荐
相关产品推荐

