如何将Child子类以Base基类形式传给WCF服务避免序列化异常?
服务器端代码:
[DataContract] public class Base { } [OperationContract] void DoSth(Base base) { }
客户端代码:
class Child : Base { }
调用DoSth(child)时触发序列化异常:
System.ServiceModel.CommunicationException: There was an error while trying to serialize parameter http://tempuri.org/:base. The InnerException message was 'Type 'Child' with data contract name 'Child:http://schemas.datacontract.org/2004/07/Client' 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.
---> System.Runtime.Serialization.SerializationException: Type 'Child' with data contract name 'Child:http://schemas.datacontract.org/2004/07/Client' 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.
需求:服务器仅用到Base的属性,不想把Child设为已知类型,希望优雅地将数据以Base对象形式发送,无需手动复制属性。尝试过给Child添加[DataContract(Name = "Base", Namespace = "http://schemas.datacontract.org/2004/07/Server")]但无效。
方案1:自定义类型转换运算符
在客户端的Child类中定义显式转换到Base的运算符,将属性复制逻辑封装在类内部,外部调用更简洁:
class Child : Base { // 示例属性,根据实际Base类属性调整 public string Name { get; set; } public int Age { get; set; } public static explicit operator Base(Child child) { return new Base { Name = child.Name, Age = child.Age }; } }
调用时直接转换:
var child = new Child { Name = "Tom", Age = 25 }; service.DoSth((Base)child);
方案2:使用AutoMapper自动映射
如果实体属性较多,手动编写转换逻辑繁琐,可借助AutoMapper自动完成Child到Base的属性映射:
- 安装AutoMapper NuGet包:
Install-Package AutoMapper - 配置映射关系:
var mapperConfig = new MapperConfiguration(cfg => { cfg.CreateMap<Child, Base>(); // 自动匹配同名属性 }); var mapper = mapperConfig.CreateMapper();
- 调用时完成映射:
var child = new Child(); var baseObj = mapper.Map<Base>(child); service.DoSth(baseObj);
方案3:自定义客户端序列化器
直接修改客户端的DataContractSerializer,强制按Base类型序列化Child对象,无需修改实体类或服务端:
var serviceClient = new YourServiceClient(); // 获取并替换客户端的序列化行为 var serializerBehavior = serviceClient.Endpoint.Behaviors.Find<DataContractSerializerOperationBehavior>(); serializerBehavior.DataContractSerializer = new DataContractSerializer(typeof(Base)); // 执行调用 serviceClient.DoSth(child); serviceClient.Close();
这种方式让序列化器忽略Child的具体类型,只序列化Base中定义的属性,服务端就能正常解析。
内容的提问来源于stack exchange,提问作者Achim Stuy

