我有一个WCF服务,它使用LinqToSql DataContext从数据库中获取一些信息。操作的返回类型是IEnumerable <DomainObject
>,我有一个助手方法,它可以从表派生的LINQ对象转换为WCF数据协定,如下所示:
[OperationContract]
public IEnumerable<DomainObjectDTO> RetrieveDomainObjects()
{
var context = CreateDataContext();
return from domainObject in context.DomainObjects
select ConvertDomainObject(domainObject);
}
private DomainObjectDTO ConvertDomainObject(DomainObject obj)
{
// etc...
}
如果我向DataContext传递了一个无效的连接字符串,则此代码会显示出一种奇怪的行为。由于无法找到正确的数据库,可能在进行序列化时,上面的代码在枚举IEnumerable <DomainObjectDTO
>时引发SqlException。然而,当我在调试器中运行这段代码时,我在服务器端根本看不到第一次出现异常!我告诉"异常"选项卡中的调试器中断所有抛出的CLR异常,但它根本没有。我也没有在Output窗口中看到"First chance exception"消息。
在客户端,我得到一个CommunicationException,其中包含一条错误消息,行数为"套接字连接意外终止"。没有任何内部异常提供任何关于问题根本原因的提示。
我能弄清楚这一点的唯一方法是重写LINQ代码,使查询表达式在OperationContract方法内部求值。顺便说一句,如果存在权限问题,或者如果我将DataContext封装在using语句中,我会得到相同的结果,所以这不仅仅是与SqlExceptions隔离的。
忽略了将返回类型设置为IEnumerable <T
>并仅枚举序列化程序深处某个位置的查询的不可取性,WCF在这种情况下是在抑制还是以某种方式防止抛出异常?如果是,为什么?
WCF似乎确实有一些神奇之处,至少在我的经验中是这样。我真的不确定它对异常做了什么,但我发现,如果FaultContract属性用于指定合约可能引发的异常,它至少会向客户端提供更多关于错误发生原因的信息。
我们还将捕获任何异常,并将其作为FaultException重新抛出,类似于:
try
{
DoSomething();
}
catch ( Exception ex )
{
throw new FaultException<CustomException>( new CustomException( ex ), ex.Message );
}
在故障合同中指定自定义异常的情况下。这似乎为客户端提供了更多有关异常的信息。所以我怀疑,如果您将SQLException作为FaultContract的一部分添加,那么它就会被发送到客户端。
在这种情况下,WCF是在抑制还是以某种方式防止抛出异常?如果是,为什么?
我对WCF了解不多,但听起来不太可能。
第一次机会异常的特殊之处在于,它直接进入调试器,调试器有机会在实际引发异常之前看到它,因此使用了"第一次机会"一词,调试器有"第一次时间"来处理异常。什么东西能绕过它?这必须是某种方式来完全阻止引发异常。(CLR中是否有安全性或可靠性功能可以做到这一点?)。
我能提出的最好的替代解释是,在您期望抛出SqlException的点之前出现了问题。我也会尝试查看非托管异常,而不仅仅是托管异常,以防出现这种情况。例如,在尝试使用托管IEnumerable的非托管序列化代码中出现问题,或者某些CLR错误。。。
您是否配置了"IncludeExceptionDetailInFaults"设置?(出于安全目的,它默认为"false")。
WCF是一个奇怪的野兽,它生活在常规IIS之外;可能是标准调试出现了故障。但是,您可以尝试启用调试异常,和/或实现(和配置)自己的IErrorHandler
来记录问题。
另一个选项:这里的组合中是否有迭代器块(yield return
)在迭代枚举器之前,迭代器块中的任何东西都不会被求值,这可能会导致本应提前验证的数据出现一些奇怪的问题。在上面的例子中,这不是一个问题(没有yield return
),但值得关注。
例如,以下是非常不同的:
var context = CreateDataContext();
var query = from domainObject in context.DomainObjects
select ConvertDomainObject(domainObject);
foreach(var item in query) { yield return item; }
尝试将以下内容放入客户端和服务的App.config中:
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Verbose,ActivityTracing"
propagateActivity="true">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="NewListener">
<filter type="" />
</add>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:App_tracelog.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="NewListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack">
<filter type="" />
</add>
</sharedListeners>
然后在Windows SDK附带的服务跟踪查看器中加载两个生成的日志文件。您将能够看到以这种方式抛出的实际异常。
链接:http://msdn.microsoft.com/en-us/library/aa751795.aspx