Debugging
When you start to get serious about building and consuming OData endpoints, there are some things you might like to know about the WCF Data Services server and client when it comes to debugging.
First and foremost, if you start having strange, unexplained interactions between your client and your development server, e.g. authentication isn’t working the way you think it ought, then make sure to switch from the “Visual Studio Development Server” (aka Cassini), which is the default, to the “Local IIS Web server.” It’s always better to test on the web server on which you’re doing to be deploy anyway and Cassini is subtly different in some ways. You can switch in the Servers section of the Web tab of your project properties. Once you get things set up – and you might have to configure the app pool in which your service is running to give it the correct permissions to say, talk to your database – you can run and debug your services just like in Cassini, so there’s no reason not to use IIS.
Now, when you’re forming your errors to send back to the client, you should prefer the use of the DataServiceException. It lets you set the HTTP status code in the response and the text message you send can be captured and reflected into the DataServiceQueryException you get on the client side. You can, of course, set the HTTP status code by setting HttpContext.Current.Response.StatusCode directly, but when you just do that, you’ll get a WebException in a .NET client and no programmatic access to the code or the description text. Further, if you throw a normal .NET exception on the server, you’ll just get a generic HTTP status code 500 (“Server Failure”) and no access to any of the exception information from the server.
Of course, by default, Data Services doesn’t expose very much exception detail anyway, as that would be a security problem. Typical exception information returned on the wire to the client looks like this:
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code></code> <message xml:lang="en-US">Server Error</message> </error>
However, if you’re under development and testing, you might want detailed information in your exception, so you can flip a switch on the server to take it up a notch:
public class ExcuseService : DataService<ExcuseContainer> { public static void InitializeService(DataServiceConfiguration config) { ... config.UseVerboseErrors = true; // DONE } ... }
With this flag set, you’re going to get server-side call stack information like so:
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code></code> <message xml:lang="en-US">Server Error</message> <innererror> <message>Exception has been thrown by the target of an invocation.</message> <type>System.Reflection.TargetInvocationException</type> <stacktrace> at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
 at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
 at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
 at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
 at System.Data.Services.Providers.BaseServiceProvider.InvokeServiceOperation(ServiceOperation serviceOperation, Object[] parameters)
 at System.Data.Services.RequestUriProcessor.CreateFirstSegment(IDataService service, String identifier, Boolean checkRights, String queryPortion, Boolean isLastSegment, Boolean& crossReferencingUrl)</stacktrace> <internalexception> <message>Server Error</message> <type>System.Data.Services.DataServiceException</type> <stacktrace> at MagicExcuseBall.ExcuseService.GetRandomExcuse() in D:\data\Programming Data\src\ch04-odata\MagicExcuseBall\MagicExcuseBall ExcuseService.svc.cs:line 61</stacktrace> </internalexception> </innererror> </error>
Don’t forget to turn this flag off before shipping your service.
One other trick you can perform when debugging your WCF Data Services endpoint is to override the HandleException method:
public class ExcuseService : DataService<ExcuseContainer> { public static void InitializeService(DataServiceConfiguration config) { ... } protected override void HandleException(HandleExceptionArgs args) { base.HandleException(args); } ... }
Whenever an exception is about to be thrown back to the OData client, this method is called in case you’d like to handle it or just be informed. I like to set a breakpoint on this method when I’m debugging just so I can see what exception is being thrown, but you can log it in your services or override it or whatever else you’d like to do.