以下是我观察到的
我需要从服务向客户端抛出一个自定义的Exception子类型。(列为特定操作的FaultContract)。我在CustomException上有一些字段,应该由客户端接收。
[Serializable]
class MyCustomException : Exception
{
public string From { get; private set; }
public MyCustomException(string where)
{
From = where;
}
}
}
我发现即使FaultException实例中存在异常,字段也没有被反序列化。我尝试通过重写GetObjectData和序列化构造函数来实现ISerializable,但没有成功。我唯一能理解的方法是将MyCustomException更改为DataContract,而不是从Exception派生。
[DataContract]
class MyCustomException
{
[DataMember]
public string From { get; private set; }
public MyCustomException(string where)
{
From = where;
}
}
这是有效的。然而,它不能再从Exception派生了。。因为Exception是用Serializable属性标记的,并且不能在一个类型上同时具有Serializable和DataContract。(已确认:引发运行时异常)
所以我的问题是:在WCF中提出自定义异常子类型的字段的正确方法是什么?
不能在一个类型上同时具有Serializable和DataContract。(已确认:引发运行时异常)
首先,您可以将DataContract
和Serializable
放在一起。事实上,如果没有这一点,我现在工作的网站将无法工作,因为我的WCF服务是通过$.ajax
在网络上使用的。此SO线程将为您提供正式的详细信息。
接下来,我强烈建议不要从Exception
继承您的自定义错误。原因是您已经有FaultException<TDetail>内置类,其中TDetail
是您的自定义错误。您可以阅读MSDN的这篇文章来了解实现的详细信息——请记住在部署时关闭客户端的"异常详细信息"。
catch (FaultException<MyFault> e)
{
//e is the full exception (with StackTrace et al.) when 'exception details' is on
//e.Detail is your custom fault which is always available
}
以下是我如何使其工作的。。不确定这是否是正确的方式。我找不到任何明确的指导原则,说明您不应该在FaultException<TDetail>
中使用Exception子类型作为TDetail参数。
我尝试抛出一个FaultException<FileNotFoundException>
,发现文件名字段的比例实际上是正确的。
那么FileNotFoundException和MyCustomException之间的差值是多少呢?
由于基本异常实现了ISerializable,我不得不重写服务器端的方法。。
protected MyCustomException(SerializationInfo info, StreamingContext context) :
base(info, context)
{
this.From = info.GetString("From");
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("From", this.From);
}
然后,我使用svcutil生成客户端代理类。我发现FileNotFoundException不是生成的(因为它是一个内置类型),而是MyCustomException。但是它没有任何字段,只有一个空的反序列化构造函数。
我之前没有深入研究ISerializable,因为当我在代理类ctor中设置断点时,它没有被击中。我错误地中止了该行的调查(未能查看类上的DebuggerStepThrough属性和GeneratedCode属性;由svcutil添加)。
因此,我手动编辑自动生成的类以添加字段并将其设置在ctor中,如…
[System.SerializableAttribute()]
public partial class MyCustomException : System.Exception
{
public string From { get; private set; } // manual edit
public MyCustomException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) :
base(info, context)
{
this.From = info.GetString("From"); // manual edit
}
}
现在它按预期工作;字段数据完好无损。
这引发了另一个问题:为什么代理生成步骤不自动执行此操作?