Ranting and raving about anything I feel like complaining about.

ADO.NET Data Services 1.5 Feature: Projections

SilverlightIf you've been following my blog, you should know that I am keeping a pretty close watch on ADO.NET Data Services. The team recently released a second CTP of the new version with some interesting features. This CTP has some pretty compelling additions, but I am going ot focus on one in particular.

I've been teaching and using ADO.NET Data Services for a long time and I like showing off exposing a LINQ-based provider (Entity Framework, NHibernate or others) to a Silverlight application. While ADO.NET Data Services does expose its API through a REST API, the magic for me is in its use in Silverlight. In case you haven't been following along, using the Silverlight client you can issue a LINQ query through the Silverlight client (though in fairness, the full power of LINQ is not supported in the client):

var qry = (from g in ctx.Games
           where g.Genre.Name == "Shooter"
           orderby g.ReleaseDate
           select g) as DataServiceQuery<Game>;

qry.BeginExecute(new AsyncCallback(r =>
{
  try
  {
    theList.ItemsSource = qry.EndExecute(r).ToList();
  }
  catch (Exception ex)
  {
    // NOOP
  }
}), null);

This is a powerful feature so that (unlike web services) developers can use a looser service definition to define their data stack. Let the developer who needs data be able to sort, filter and shape that data as necessary.

The last part of that phrase is "shape" on purpose. When I first teach this technology, I am often asked "what sort of shaping do you mean?" In ADO.NET Data Services 1.0, you could shape it by returning a hierarchy of data using the Expand method in the LINQ query. For example, we can return the game data with the Genre entity attached to every game like so:

var qry = (from g in ctx.Games
                        .Expand("Genre")
           where g.Genre.Name == "Shooter"
           orderby g.ReleaseDate
           select g) as DataServiceQuery<Game>;

qry.BeginExecute(new AsyncCallback(r =>
{
  try
  {
    theList.ItemsSource = qry.EndExecute(r).ToList();
  }
  catch (Exception ex)
  {
    // NOOP
  }
}), null);

But this level of shaping is powerful but limited in scope. This is where ADO.NET Data Services 1.5 comes up trumps. In this new CTP we can now do projections to retrieve specific data. For example:

var qry = (from g in ctx.Games
           where g.Genre.Name == "Shooter"
           orderby g.ReleaseDate
           select new
           {
             Name = g.Name,
             ReleaseDate = g.ReleaseDate.GetValueOrDefault(),
           }) as DataServiceQuery;

This query creates a new anonymous type that has the two values we want instead of returning the entire entity. In any other .NET project this would be fine, but for Silverlight we can't use the anonymous type for data binding (for security reasons). So like other anonymous type uses in Silverlight, you must project them into a known type.  A type like that is pretty easy to cruft up if you need:

var qry = (from g in ctx.Games
           where g.Genre.Name == "Shooter"
           orderby g.ReleaseDate
           select new GameInfo()
           {
             Name = g.Name,
             ReleaseDate = g.ReleaseDate.GetValueOrDefault()
           }) as DataServiceQuery<GameInfo>;

This is useful to no only limit the fields you return but also to shape the result.  For example we can use the projection to retrieve the name, release date and the genre's name (normally a related object) like so:

var qry = (from g in ctx.Games
           where g.Genre.Name == "Shooter"
           orderby g.ReleaseDate
           select new GameInfo()
           {
             Name = g.Name,
             ReleaseDate = g.ReleaseDate.GetValueOrDefault(),
             GenreName = g.Genre.Name
           }) as DataServiceQuery<GameInfo>;

This is all well and good, but what about saving these projections? For the most part you should treat complex projections (like the one above) as read-only. But if you retrieve entities that are purely subsets of existing entities, then ADO.NET Data Services will handle it.  Though currently this is crashing in Silverlight (it is a CTP after all). I will update this if I find a way around the saving bug in the near future and post the demo at that time.

But in general this is an awesome new addition to the library that I hope will close some of the use-cases that forced me to write Service Operations in the past.

What do you think?

 
 

Comments

Gravatar Nice write up Shawn. The saving issue you are hitting is a known issue we have with CTP2 on silverlight. It should work on the desktop.
Gravatar I have a question. Ria Services will replace Ado Data Services?
Gravatar I don't think so. I still think that ADO.NET Data Services is the more powerful, less hard coded model. RIA Services will have it place but my casual look at it still makes ADO.NET Data Services a viable solution that uses less magic to me.
Gravatar However RIA still has a more powerfull model than ADO.Net DataServices ... you should give it a more in dept look.
Gravatar I think the best approach is to use RIA service and Domain ADO.NET DataServices that consume/expose the ria service.
Gravatar Pop,

It is a powerful model, but mostly powerful at design time, not runtime. I've given RIA a long look and I think of it more like LINQ 2 SQL, easier dev story but shorter customization/tweaking.
Gravatar nisbus,

I still don't see the DataServer->RIA benefit. Why go across two serialization layers?
Gravatar Aren't RIA-Services and ADO.NET Data Services supposed to be getting combined into one unified system eventually by MS?
Gravatar That's what I hoped, but with schedules being what they are, it looks like interoperability not merging is going to happen in this next release.
Gravatar Can you please share the code? I am trying to reference Orders in query by using Expand and create a new object which is a combination of customer and orders information.... you can send me an e-mail.
Gravatar Shawn,if possible? Could you request Microsoft move the Validation/Regular Expression plumbing into the Data Services.
Gravatar Rashun,

You can request it by going to the Silverlight.net site and posting it on the bug forum. I've already asked but we need more and more people asking for it.

Currently my Niagara project is attempting to do it, but its in the very early stages and we're being roadblocked by the Silverlight framework a bit.
Gravatar Hi Shawn,
When I do this on Vs2010 I get the following error An item could not be added to the collection. When items in a DataServiceCollection are tracked by the DataServiceContext, new items cannot be added before items have been loaded into the collection.
This happens on this line
var query = (DataServiceQuery<Post>)ar.AsyncState;
var entities = query.EndExecute(ar).ToList();
Any ideas?
Gravatar Philip,

Most likely this is because you're doing SL3 development. The new projections are not supported in SL3 even in VS2010.

Add a Comment

*
*
*