Silverlight Cookbook: WCF Data Services and NHibernate

This post is part of a series of blog posts detailing various aspects of the Silverlight Cookbook, an initiative to demonstrate proper design choices for building enterprise-class line-of-business applications with Silverlight (and WPF if you will). It currently consists out of the following parts.

In the Silverlight Cookbook I use a variation of the CQRS pattern where business-oriented commands are used to create, update or change the data, while WCF Data Services (formally ADO.NET Data Services) is used as a general-purpose query service.

Although WCF Data Services has been designed with the Entity Framework in mind, my desire to use an advanced ORM with features like 2nd level caching has let me to Shawn Wildermuth's attempt to support NHibernate as a WCF Data Service provider. Unfortunately, that solution no longer works with NHibernate 3.x, but I managed to get it working again by compiling my own version. But regardless of all the goodness of NHibernate 3, not everything went as smooth as I hoped for.

For starters, you need to expose all entities as entity sets even though you use them only in associations with another parent entity and never query on them. Consider for instance that we have a Recipe entity that has an aggregate relationship with one or more Rating entities. We'll never query directly on the ratings, but we still need to expose them like this:

Another thing is that WCF Data Services chokes on the proxies that NHibernate generates at run-time. So if your entity lazy-loads another associated entity, you have to disable lazy-loading altogether. I've actually spent quite a few hours on using Reflector to investigate whether the NHibernateContext or DataService classes could be tweaked to properly map the derived proxy class back to the appropriate entity type, but failed. As a workaround, you either need to to disable lazy-loading in the mapping, or eagerly fetch the associations using the Fetch extension method introduced in NHibernate 3: publicclassRestContext : NHibernateContext { publicIQueryable<Recipe> Recipes { get { returnSession .Query<Recipe>() .Fetch(r => r.Ratings); } } }

Unfortunately, that introduces another 'feature' of NHibernate. I've never quite understood why, but when NHibernate fetches one-to-many and many-to-many associations, you end up with duplicate records in the final result set. Just appending a Distinct() to the entity set getter such as the one above doesn't help. This has actually cost me a lot of time to work out, because when loading a lot of entities, the result set ended up to be too big for WCF Data Services to transfer. You'll find the solution in a hidden feature of NHibernate.Linq that allows you to add a custom action to an IQueryable expression tree like this:

Since I don't do any updates through the query service, I haven't tried how well CRUD operations through WCF Data Services work. However, I did notice that in order to receive the latest changes from the server, I have to make sure to set the MergeOption property of the generated DataServiceContext subclass to MergeOption.OverwriteChanges:

In the default setting, MergeOption.AppendOnly, it will assume all changes are made by the client, and ignore anything that occurred on the server (beats me why it does that). You can also use MergeOption.NoTracking and get the same behavior, but with MergeOption.OverwriteChanges, two queries with the same key (identified by the [DataServiceKey] attribute) will return the same physical object. This is quite convenient when binding a ComboBox to the list of available entities and the currently selected one.

To conclude this post, let me tell you that debugging WCF Data Services is definitely cumbersome. When something goes wrong or is incorrectly configured you'll only receive a generic error like this:

You can solve that by slapping a [ServiceBehavior] attribute to your data service class like this:

Or you can add the corresponding configuration setting to the <behaviors> element of your web.config:

<serviceBehaviors> <behaviorname=""> <serviceMetadatahttpGetEnabled="true" /><serviceDebugincludeExceptionDetailInFaults="true" /></behavior></serviceBehaviors> Finally, you can force WCF Data Services to include a bit more info on the exception that occurred by changing the UseVerboseErrors property of the DataServiceConfiguration class.

Share on

Leave a Comment

You May Also Enjoy

It may be coincidence, but the two best tutorials I attended at Agile DevOps East both ran on the same day. The first one focused mostly on agile transformation, but briefly touched on the leadership topic. This one, let by Jennifer Bonine, took this further by focusing on being a better leader b...

For my annual conference trip, I decided to skip the always-great QCon conference for once and instead attend Agile DevOps East in Orlando, Florida. In addition to the typical conference schedule, I also registered for some of the half-day and full day tutorials. One of them, How to lead high-per...

Transient vs non-transient exceptions
If I have to name the single biggest flaw in adopting Event Sourcing, it must be our decision to rely on the synchronous dispatching pipeline of NEventStore. It is based on the idea that every event will be processed by all projectors in a synchronous manner....