Object projection with WCF RIA Domain Services and Domain Data Source

I have just been tasked with looking into a new way to architect one of our main ASP.NET applications tiers, since we are moving it to Entity Framework 4. There are a few goals that we would like to achieve:

  • Separation of Concerns – not tying any data logic into the UI
  • Ease of use – we should not have to jump through a million hoops to bind a control to data
  • Accurate projection – only retrieve the fields necessary for the current control.

So SOC makes us throw EntityDataSource out the window straight away.

A repository structure designed to either be directly queryable or bound to an ODS is feasible, but always requires a lot of overrides.

Scouring around the blogosphere, a lot of interest seems to be directed at using WCF RIA Domain Services and Domain Data Source to achieve separation of concerns while also keeping things neat and simple.

So I install the requisite packages from http://www.silverlight.net/getstarted/riaservices/ (The services package AND the toolkit, which holds the Domain Data Source), and delve in to have a play.

I create a quick Entity Data Model from Northwind (I’m not going to go into how here, there are plenty of blogs about that), then add a new Domain Service Class called CustomerService, which I intend to use to query some customer info from my EDM. A small wizard appears and asks you which context you would like to use (The one I just created), and which entities you would like in this service, and whether they should be editable or not. What you end up with is a class that looks like so:

Code Snippet
  1. public class CustomerService : LinqToEntitiesDomainService<NorthwindEntities>
  2.     {
  3.  
  4.         // TODO:
  5.         // Consider constraining the results of your query method.  If you need additional input you can
  6.         // add parameters to this method or create additional query methods with different names.
  7.         // To support paging you will need to add ordering to the 'Customers' query.
  8.         public IQueryable<Customer> GetCustomers()
  9.         {
  10.             return this.ObjectContext.Customers.OrderBy(x => x.CustomerID);
  11.         }
  12.  
  13.         public void InsertCustomer(Customer customer)
  14.         {
  15.             if ((customer.EntityState != EntityState.Detached))
  16.             {
  17.                 this.ObjectContext.ObjectStateManager.ChangeObjectState(customer, EntityState.Added);
  18.             }
  19.             else
  20.             {
  21.                 this.ObjectContext.Customers.AddObject(customer);
  22.             }
  23.         }
  24.  
  25.         public void UpdateCustomer(Customer currentCustomer)
  26.         {
  27.             this.ObjectContext.Customers.AttachAsModified(currentCustomer, this.ChangeSet.GetOriginal(currentCustomer));
  28.         }
  29.  
  30.         public void DeleteCustomer(Customer customer)
  31.         {
  32.             if ((customer.EntityState == EntityState.Detached))
  33.             {
  34.                 this.ObjectContext.Customers.Attach(customer);
  35.             }
  36.             this.ObjectContext.Customers.DeleteObject(customer);
  37.         }
  38.     }

Alright, looks good! Note that I added the OrderBy after getting an exception when binding the first time, turns out when you want it to perform paging your query needs to specify the order manually.

Next I go about setting up my page. I add a Domain Data Source, specify the necessary fields (The important ones at the moment being DomainServiceTypeName, which points to my CustomerService Domain Service, and QueryName, which names the GetCustomers method). I also add a Telerik RadGrid, set Paging and Sorting on, and set AutoGenerateColumns off. I specify three bound columns – CompanyName, ContactName and ContactTitle. The reason I am doing this is because I want to see if projection works – that the objects bound to my grid only contain the data I am binding to.

Next I fire up SQL profiler so I can analyse exactly what data is retrieved during binding, then fire up my application, and LO AND BEHOLD – it retrieves ALL the data relating to the object. Projection has not occurred.

After a little digging, the reason for this is quite obvious. When the Domain Data Source is binding to data, you might be forgiven for thinking it calls and returns the results of your Domain Service’s GetCustomers() method. It doesn’t. It in fact calls the Domain Service’s Query method, which in turn invokes your GetCustomers() method. The only problem with this is that Query returns IEnumerable. So your object graph is enumerated before it is returned – and therefore it’s contents are fully populated.

Just a very small gripe also – you cannot overload the select method. If you want another method, say with a customerID parameter, you need to create a method with another name, such as GetCustomerByID. This might not be the best example of why this is restrictive, but I am sure there are other implementations in which this would be useful.

Ah well, back to the drawing board :)

Print | posted on Friday, 20 August 2010 3:04 PM

Feedback

No comments posted yet.
Title  
Name
Email (never displayed)
Url
Comments   
Please add 3 and 1 and type the answer here: