May 26 2011

Consuming an OData Feed using MonoTouch

After having great success consuming a WCF Web service using MonoTouch as I mentioned in the previous post, I next set off to consume an OData feed thinking this would be just as easy. And sure enough, it was just as easy, until I graduated my wonderful little app from the iPhone Simulator to my actual phone. Once on my phone, the application would still run just fine, but whenever a call was made to asynchronously fetch data from the OData feed – crickets – my user friendly activity indicators just keep spinning indefinitely.

Here are the steps I took to get this far:

  1. Copied into my MonoTouch project the generated data service reference class from the WP7 project where I previously consumed the OData feed
  2. Referenced the System.Data.Services.Client assembly provided by MonoTouch
  3. Put together the following method to asynchronously retrieve a single entity from the OData feed:
public void GetMarker(int markerNum, Action<Marker> successCallback, Action<Exception> errorCallback)
{
	var serviceUri = new Uri(ODataServiceUrl);
	var dataServiceContext = new MyDataServiceContext(serviceUri);
	var markerUri = new Uri(string.Format("/Marker({0})", markerNum), UriKind.Relative);
 
	try
	{
		dataServiceContext.BeginExecute<Marker>(markerUri, a =>
		{
			try
			{
				var results = dataServiceContext.EndExecute<Marker>(a);
				var marker = results.Single();
				successCallback(marker);
			}
			catch (Exception e)
			{
				errorCallback(e);
			}
		},
		null);
	}
	catch (Exception e)
	{
		errorCallback(e);
	}
}

As you can see by reviewing the above code, you would think that it is guaranteed that one of the two callbacks (either the successCallback or errorCallback) will always be called. However, launching the Mono Soft Debugger proved otherwise. I placed a breakpoint inside the anonymous delegate passed to the BeginExecute method, and sure enough, it is never reached.

Dumbfounded by what was happening, I rewrote the method to make the call synchronously by replacing the call to BeginExecute with its synchronous counterpart, Execute. It was then that I finally got some visibility into my original issue because the underlying exception was now being caught:

Attempting to JIT compile method '(wrapper managed-to-native) System.Threading.Interlocked:CompareExchange
(System.Exception&,System.Exception,System.Exception)' while running with --aot-only.
  at System.Data.Services.Client.BaseAsyncResult.HandleFailure (System.Exception e) [0x00000] in :0
  at System.Data.Services.Client.QueryResult.Execute () [0x00000] in :0
  at System.Data.Services.Client.DataServiceRequest.Execute[Marker]
(System.Data.Services.Client.DataServiceContext context, System.Data.Services.Client.QueryComponents
queryComponents) [0x00000] in :0
  at System.Data.Services.Client.DataServiceContext.Execute[Marker] (System.Uri requestUri) [0x00000] in :0
  at Trentacular.Trsh.MarkerService.GetMarkerUsingDataServiceContext (Int32 markerNum, System.Action`1
successCallback, System.Action`1 errorCallback) [0x0002a]

I then found this entry in the MonoTouch troubleshooting documentation that I believe is suggesting that I need to explicitly force the AOT compiler to include the Interlocked.CompareExchange method by calling it myself before the call to Execute. Tried that, same exception still, except it is now getting thrown when I explicitly called the CompareExchange method.

Running out of options, I ditched the DataServiceContext altogether and rewrote the GetMarker method using a WebClient and Linq-to-Xml as follows:

static readonly XNamespace atomNS = XNamespace.Get("http://www.w3.org/2005/Atom");
static readonly XNamespace metadataNS = XNamespace.Get("http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
static readonly XNamespace dataservicesNS = XNamespace.Get("http://schemas.microsoft.com/ado/2007/08/dataservices");
 
public void GetMarker(int markerNum, Action<Marker> successCallback, Action<Exception> errorCallback)
{
	var webClient = new WebClient();
	webClient.DownloadStringCompleted += delegate(object sender, DownloadStringCompletedEventArgs args)
	{
		try
		{
			if (args.Error != null)
			{
				errorCallback(args.Error);
				return;
			}
 
			var document = XDocument.Parse(args.Result);
			var root = document.Root;
			var properties = root.Element(atomNS + "content").Element(metadataNS + "properties");
 
			var marker = new Marker
			{
				MarkerNum = Convert.ToInt32(properties.Element(dataservicesNS + "MarkerNum").Value),
				Address = properties.Element(dataservicesNS + "Address").Value.TrimEnd(),
				YearEstablished = properties.Element(dataservicesNS + "Year").Value,
				MarkerText = properties.Element(dataservicesNS + "MarkerText").Value
			};
 
			successCallback(marker);
		}
		catch (Exception e)
		{
			errorCallback(e);
		}
	};
 
	try
	{
		var markerUri = new Uri(string.Format("{0}/Marker({1})", ODataServiceUrl, markerNum), UriKind.Absolute);
		webClient.DownloadStringAsync(markerUri);
	}
	catch (Exception e)
	{
		errorCallback(e);
	}
}

This worked liked a charm. It is unfortunate though the DataServiceContext approach doesn’t work. While Linq-to-Xml isn’t all that bad, I am having to do more work than my spoiled .Net developer mentality would like – why traverse xml, deal with xml namespaces, and require knowledge of node names when this can all be abstracted for you.

Knowing the Mono guys are no longer working for Novell, I doubt this will ever get fixed in MonoTouch, but I am hoping that they will produce something far more exceptional with their new venture Xamarin.


May 13 2011

iOS and WCF – Better Together Thanks to MonoTouch

I was recently presented with an opportunity to write a mobile application for a local Dallas homeless ministry. Learning about what the ministry leader envisioned for the app as well as already owning a Mac and an iPhone led me down the iOS development path. After reading a little about Objective-C and memory management, I quickly was loosing hope for turning something around before having to go back to work Monday. I guess I’ve been spoiled for too long with automatic garbage collection.

I then learned about MonoTouch, downloaded the trial, got up and running with MonoDevelop, and one evening later had finished my first application. To get my feet wet, I chose to port a Windows Phone 7 application my colleagues at Slalom and I had written called Texas Roadside History (shameless plug: if you use a WP7, search for Roadside History in the Marketplace).

I want to highlight a single HUGE benefit I’ve realized to having used MonoTouch: I get the WCF stack!

The server component of Texas Roadside History is running in Azure – SQL Azure and a Web role providing both a SOAP Web service endpoint and OData endpoints. Having already generated service references (client proxies) for these endpoints in the WP7 version of the app, I simply copied them into my MonoTouch project, referenced the System.ServiceModel assembly, and was back to making asynchronous Web service calls with zero XML parsing.