幸运的是,Message 属性是虚拟的,而我需要的其他属性具有公共设置器,因此我可以禁用继承的(反)序列化代码:
[Serializable]
public abstract class MyErrorBase : Exception
{
protected string reason;
public string TrackingID { get; set; }
public override string Message
{
get { return reason; }
}
public MyErrorBase() : this(null) { }
public MyErrorBase(string reason) : base(reason)
{
this.reason = reason;
// other init logic
}
protected MyErrorBase(SerializationInfo info, StreamingContext context)
{
HelpLink = info.GetString("HelpLink");
Source = info.GetString("Source");
TrackingID = info.GetString("TrackingID");
reason = info.GetString("Reason");
}
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Reason", reason);
info.AddValue("TrackingID", TrackingID);
info.AddValue("Source", Source);
info.AddValue("HelpLink", HelpLink);
}
}
令人惊讶的是,在重新抛出反序列化实例时,禁用继承逻辑对于 CLR 来说绝对没问题。
对于那些对我为什么需要这个感兴趣的人。我的自定义 WCF 错误处理受到了这个很棒的 article 的启发。因此,我的故障不是来自简单的普通故障类,而是来自MyErrorBase,我的操作合同如下所示:
[OperationContract]
[FaultContract(typeof(InvalidState))]
void Action();
其中InvalidState 继承自MyErrorBase。这很好用,因为Exception 标有[Serializable] - 这对于DataContractSerializer 来说已经足够了。但是您不希望由于服务调用而暴露堆栈跟踪等任何细节,对吗?
生成的信封(现在)如下所示:
<s:Fault>
<faultcode>s:InvalidState</faultcode>
<faultstring xml:lang="en-US">Cannot be processed in its current state.</faultstring>
<detail>
<InvalidState xmlns="http://schemas.datacontract.org/2004/07/MyNS" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
<Reason i:type="x:string" xmlns="">Cannot be processed in its current state.</Reason>
<TrackingID i:type="x:string" xmlns="">50547779-06d4-470d-ae75-a9feb3e08a99</TrackingID>
<Source i:type="x:string" xmlns="app1"/>
<HelpLink i:type="x:string" xmlns="">blabla</HelpLink>
<State i:type="x:int" xmlns="">2</State>
</InvalidState>
</detail>
</s:Fault>
最大的好处是非 .NET 消费者仍然可以正常工作(完全兼容 SOAP,我们不会暴露敏感数据),但服务的 .NET 消费者现在可以将错误提取为 .NET 异常并重新抛出它客户端(或以通常方式处理故障)。这比处理FaultException<T> 要好得多,因为您可以将故障组织为层次结构并享受捕获块中的继承。 FaultException<T> 在这里失败,因为类型(in)方差。