Working with Projections and DTOs in WCF Data Services

For those of you who haven’t been following “Project Astoria”, you’ve been missing out on some pretty exciting technology. WCF Data Services (formerly known as ADO.NET Data Services) is a stack on top of WCF that enables the creation and consumption of REST-based data services for the web. There are several intriguing features in the upcoming release that coincides with .NET Framework 4, including:

  • Projections: Since CTP2, the Data Services URI API supports the ability to work with a subset of properties on an Entity.
  • Data Binding: The client library now supports two-way data binding for applications built with Silverlight or WPF technologies.
  • Row Count: You can now retrieve the total number of entities in a set, without having to fetch all of the entities within that set.

The ability to create projections and shape your data directly on the URL can be quite useful. Oftentimes, only a few properties of an entity are necessary, and WCF Data Services makes it easy to achieve an efficient query that returns precisely the data you need. For an example, take the following Entity Framework data model:

Example Entity Model

Let’s say you only wanted to return a list of Employee Names, along with the City they live in. If exposed via a WCF Data Service, you could could get exactly this data by using the $select parameter in your querystring:

http://domain/data.svc/Employees?$select=Id,FirstName,LastName,Address/Id,Address/City&$expand=Address

This request will only return the Employee’s Id, FirstName, and LastName and the Address’s Id, and City. The $expand query parameter with the Address value is telling the service to eager load the Address object, which is necessary be able to return the projected properties that we need from it. Any developer who has been creating AJAX-intensive websites will tell you that ability to achieve this granularity without any extra work is extremely useful.

More Complex Scenarios

The projections and expansion features are extremely useful, but at some point you will undoubtedly run into a scenario that isn’t supported via the querystring API. WCF Data Services allows you to expose custom operations on your service to facilitate these scenarios. Using example data model above, let’s say we would want the Employee’s Id, Name, and the Count of the OptionsApprovals from the StockOptionApproval relationship. Since there is currently no way to project the count of a child relationship in the querytstring API (don’t misunderstand the $count parameter as I first did; it will not help here), you would need to expose a custom service operation. Intuitively, you might code something like this:
[csharp]
[WebGet]
public IEnumerable<EmployeeOptionApprovalCountDTO> EmployeesWithOptionApprovalCount()
{
var employees = CurrentDataSource.Employees
.Select(x => new EmployeeOptionApprovalCountDTO {
x.ID,
x.Name,
OptionsApprovalCount = x.Children.Count()
})
.ToList();

return employees;
}
[/csharp]

Unfortunately, this will not work. At least not when your WCFService inherits from an Entity Framework ObjectContext:
[csharp]
public class EmployeeService : DataService<EntityContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
// more config options here…
}
}
[/csharp]

When your service inherits from an ObjectContext, WCF Data Services will use the ObjectContextDataProvider. A current limitation is that you can only return entities from the custom service operations that you expose. In other words, if you try to return a data transfer object (regular CLR class), your service will fault with:

'Unable to load metadata for return type 'System.Collections.Generic.IEnumerable`1[EmployeeOptionApprovalCountDTO]' of method 'System.Collections.Generic.IEnumerable`1[EmployeeOptionApprovalCountDTO] GetEntityWithCount(Int32)'

Disappointingly, you can’t even use the config’s RegisterKnownType method during InitializeService, because it isn’t used when ObjectContextDataProvider is chosen as the Provider for your service. So what other options do you have for this seemingly straightforward use case?

The next thing I tried was to create an Entity in the model that isn’t mapped to a table in the database. This was a dead end, because when using the unmapped entity in the entity data model as the DTO to return from the service, I received this exception:

The server encountered an error processing the request. The exception message is 'Service operation 'UnmappedEntityDTO' produces instances of type 'UnmappedEntityDTO', but there are no visible entity sets for that type. The service operation should be hidden or a resource set for type 'UnmappedEntityDTO' should be made visible.'.

So what are we left with to try? Fortunately, the entity framework has the concept of complex types, which technically are to be used as properties of entities. However, the ObjectContextDataProvider will allow you to return a complex type in the EntityDataModel from your service operation. You can create them in the Model Browser:

example-ef-complex-type

As if jumping through all those hoops weren’t enough, there is one more gotcha to consider. When building a Linq to Entities query, you cannot use a complex type as part of your where clause! You will receive the following exception:

The entity or complex type 'ComplexTypeAsDTO' cannot be constructed in a LINQ to Entities query.

So instead, you must first use an anonymous type, invoke ToList() to ensure your query gets executed, and then finally transform your anonymous type to your complex type so that your service is able to return your objects:
[csharp]
return CurrentDataSource.Employees
.Select(x => new{
Id = x.Id,
Name = x.Name,
OptionsApprovalCount = x.OptionsApprovals.Count
})
.ToList()
.Select(x => new EmployeeWithOptionsApprovalCount {
Id = x.Id,
Name = x.Name,
OptionsApprovalCount = x.OptionsApprovalCount
});
[/csharp]

Summary

WCF Data Services enables rapid development and the ability to easily expose your entity model. And when combined with the entity framework in conjunction with it’s new POCO support, you don’t have to sacrifice your n-tier architecture and proper separation of concerns. However, there are still some counterintuitive practices that must be used in order to handle seemingly-basic scenarios. Hopefully, by the time the final product is delivered, there will be better support for these situations. But for now, I’m happy that I found a decent workaround!

12 thoughts on “Working with Projections and DTOs in WCF Data Services

  1. Thanks Sam – finally, a comprehensive understandable explanation. Pity the new MS architecture seems so broken, It’s no wonder I havn’t been able to find a good architecture guidance options for how to build silverlight data apps – lots of little over simplified snippets everywhere instead.

    I’m looking forward to it all working (hopefully) pretty smoothly when we get to a final release of 4.0

    cheers
    ewart

  2. So, has the scene changed any , with the latest WCF Data services release ?

    It’s hard to find articles that discuss this common scenario, don’t know why, but glad you addressed it.

  3. Pingback: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>