.NET 8 Connected Service WCF客户端能否设置非标准默认值?
问题概述
将Xamarin.Android/iOS应用迁移至.NET 8后,使用Connected Service生成的WCF客户端不再生成带Specified后缀的属性。但在部分场景下(如对接未更新契约的旧版本服务,或服务端设置EmitDefaultValue=false),需要在服务未返回布尔字段值时,将其默认值设为True而非.NET默认的False,替代旧客户端中dto.World = dto.WorldSpecified ? dto.World : true;的逻辑。
可行解决方案
1. 利用Partial类的反序列化回调(推荐)
生成的WCF DTO类均为partial,可以在独立的代码文件中扩展类,通过OnDeserializing回调在反序列化前设置默认值。这种方式不会被服务引用更新覆盖,维护性强。
创建一个与DTO同名的扩展文件(如TestDTO.Extensions.cs),添加以下代码:
public partial class TestDTO { [System.Runtime.Serialization.OnDeserializing] private void OnDeserializing(System.Runtime.Serialization.StreamingContext context) { // 反序列化前将字段初始化为True // 若服务返回了World值,会覆盖此初始值;若未返回,则保留True WorldField = true; } }
原理:WCF的DataContractSerializer在反序列化对象时,会先执行标记了OnDeserializing属性的方法,之后才会将从服务端接收到的数据赋值给字段。因此如果服务未返回World字段,字段会保持我们设置的True值;若服务返回了该字段,则会覆盖初始值。
2. 修改Connected Service生成配置,添加默认值属性
可以在添加/更新Connected Service时,通过配置让生成的DataMember属性包含DefaultValue设置,结合EmitDefaultValue=false实现需求:
- 在添加Connected Service的高级设置中,找到“DataMember属性”相关配置,勾选“生成默认值”;
- 或者手动编辑Connected Service的配置文件(如
ConnectedService.json),添加SvcUtil参数:{ "extendedData": { "inputs": [ "http://test.wcf.service/IMobileCommunicationService?wsdl" ], "options": [ "/dconly", "/namespace:*,SalesPad.DataCollection.Service", "/defaultValue:true" ] } } - 更新服务引用后,生成的
DataMember会带有DefaultValue(true)属性:[System.Runtime.Serialization.DataMemberAttribute(DefaultValue=true)] public bool World { get { return this.WorldField; } set { this.WorldField = value; } }
注意:此方法需要确保服务端对应字段的EmitDefaultValue设置为false,否则当服务返回False时,客户端会将其视为未指定而使用默认True,导致逻辑错误。
3. 封装DTO的访问逻辑
如果上述方法不适用,可以封装一个访问器方法,统一处理默认值逻辑:
public partial class TestDTO { private bool _worldAssigned; [System.Runtime.Serialization.OnDeserialized] private void OnDeserialized(System.Runtime.Serialization.StreamingContext context) { _worldAssigned = true; } public bool GetWorldValue() { return _worldAssigned ? WorldField : true; } }
之后在业务代码中使用dto.GetWorldValue()替代直接访问dto.World。
内容的提问来源于stack exchange,提问作者Jonathon Minard

