views:

488

answers:

4

I've got a WCF service that uses a LinqToSql DataContext to fetch some information out of a database. The return type of the operation is IEnumerable<DomainObject>, and I have a helper method that converts from the Table-derived LINQ object to a WCF data contract like so:

[OperationContract]
public IEnumerable<DomainObjectDTO> RetrieveDomainObjects()
{
    var context = CreateDataContext();
    return from domainObject in context.DomainObjects
           select ConvertDomainObject(domainObject);
}

private DomainObjectDTO ConvertDomainObject(DomainObject obj)
{
    // etc...
}

This code exhibits a strange behaviour if I pass an invalid connection string to the DataContext. Being unable to find the correct database, presumably the above code throws a SqlException when enumerating the IEnumerable<DomainObjectDTO> when serialization is happening. However, when I run this code in my debugger, I see no first-chance exception on the server side at all! I told the debugger in the Exceptions tab to break on all thrown CLR exceptions, and it simply doesn't. I also don't see the characteristic "First chance exception" message in the Output window.

On the client side, I get a CommunicationException with an error message along the lines of "The socket connection terminated unexpectedly". None of the inner exceptions provides any hint as to the underlying cause of the problem.

The only way I could figure this out was to rewrite the LINQ code in such a way that the query expression is evaluated inside of the OperationContract method. Incidentally, I get the same result if there is a permissions problem, or if I wrap the DataContext in a using statement, so this is not just isolated to SqlExceptions.

Disregarding the inadvisability of making the return type an IEnumerable<T> and only enumerating the query somewhere in the depths of the serializer, is WCF suppressing or somehow preventing exceptions from being thrown in this case? And if so, why?

A: 

Did you configure your "IncludeExceptionDetailInFaults" settings? (it defaults to 'false' for security purposes).

Timothy Khouri
A: 

WCF is a curious beast that lives slightly outside of regular IIS; it may be that the standard debugging gets glitchy. However, you could try enabling debug exceptions, and/or implementing (and configuring) your own IErrorHandler to log the issues.

Another option: is there perhaps an iterator block (yield return) somewhere in the mix here? Nothing in an iterator block is evaluated until the enumerator is iterated, which can lead to some odd looking issues with data that should have been validated earlier. In the case above, this isn't an issue (no yield return), but it is one to watch.

For example, the following is very different:

var context = CreateDataContext();
var query = from domainObject in context.DomainObjects
       select ConvertDomainObject(domainObject);
foreach(var item in query) { yield return item; }
Marc Gravell
+1  A: 

WCF does seem, at least in my experience, to do some magic with exceptions. I'm really not sure what it does with exceptions but I've found that if the FaultContract attribute is used to specify exceptions that the contract could throw, it'll at least give a bit more information to the client about the why the error occurred.

We would also catch any exceptions and re throw them as FaultExceptions, something like:

try
{
    DoSomething();
}
catch ( Exception ex )
{ 
   throw new FaultException<CustomException>( new CustomException( ex ), ex.Message );
}

Where custom exception is specified in the fault contract. This seems to give a bit more information to the client about the exception. So I suspect if you add SQLException as part of the FaultContract then it'll get sent to the client.

Darren
A: 

Try putting the following in the App.config for your client and service:

<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>

Then load both the resulting log files in the Service Trace Viewer that comes with the Windows SDK. You will be able to see the actual exception thrown that way.

Links: http://msdn.microsoft.com/en-us/library/aa751795.aspx

Emjay