К счастью, свойство 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 вдохновлена этой замечательной статьей .Таким образом, вместо тривиальных классов простых ошибок мои ошибки получены из 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).