12
Dec
07

ADO.NET Data Services Part 1: Services (contd.)

12/30/2007 - I made modifications throughout the entire article

Before moving on to part 2 of the series, I wanted to stress two points that I realized might not have been 100% clear from the last post, and are absolutely pivotal to understanding the full beauty of ADO.NET Data Services:

  1. ADO.NET Data Services is in no way coupled to relational data
  2. Data services come in different flavors, based on their respective data sources

1 - Data != Database

It is very important to understand that the “Data” in “Data Service” doesn’t mean “Database”, it literally just means “Data”, of any kind. ADO.NET Data Services work with types that implement IQueryable<T>. It is as basic and simple as that. The reason why data services work with LINQ To SQL out of the box is because the generated DataContext class exposes the individual entities as instances of type Table<T>, which in turn implement IQueryable<T>.

Here is the generated property on my DataContext (note the return type):

iuhih.JPG

Here is the hierarchy of the Table<T> class (note the presence of IQueryable<T>):

astoriaaddition2.JPG

As you can see there is no magic to why LINQ To SQL and ADO.NET Data Services get along so well. It’s all about IQueryable<T>.

The reason that data services work with the Entity Framework is because the generated ObjectContext exposes the individual entities as an instance of type ObjectQuery<T>, which in turn implements IQueryable<T>.

What this means is that any new technology or data provider that Microsoft (or any third-party vendor) create can be fully consumed by a data service as long as the provider exposes its data using a derivative of IQueryable<T>. This is the “Euraka” point of data services where you begin to see how beautiful they really are :)

You might be thinking to yourself right now, “You know, I recall seeing an extension method called AsQueryable added in .NET 3.5 that applies to objects of type IEnumerable<T>. Does that mean I could convert any enumerable list into an IQueryable<T> instance and it be exposable via a data service?” The answer to that question would be yes. The entire slew of IEnumerable<T> implementing classes in .NET can now have their contents exposed and abstracted through a data service just by calling one method (AsQueryable). I don’t know about you but that point makes me want to just cry.

I bet you’d like to see this in action? Let’s create an extremely basic replication of the same cell phone model/service we used in the last post, but without getting LINQ To SQL involved.

I create a CellPhone class:

astoriaaddition10.JPG

Based on our experience from the last post, we know that every object exposed through a data service has to have an identifiying property. For info on the criteria, read the previous post. For now I’m just going to apply the DataWebKey attribute to the ModelName property (just like I did in the last post):

astoriaaddition9.JPG

That’s all we have to do with a CellPhone class. Now we have to create a container class that will house our CellPhone data and expose it as an IQueryable<T> instance.

astoriaaddition8.JPG

As you can see there is no magic going on here. CellPhoneContainer simply manages the creation of the list of CellPhones as well as the exposure of it as an IQueryable<T> (which is ridiculously easy to do). An important thing to note here is that although I’m statically filling the collection of CellPhone object’s in the container’s constructor, your imagination could really run free here. I could be reading the data from a file, XML, the event log, a web service, etc. The point is that the data can come from anywhere that logically makes sense. ADO.NET Data Services are not synonymous with any type of data.

Let’s create our service and specify the CellPhoneContainer as its data source. I’ll also turn on access rights for the entire container.

astoriaaddition11.JPG

Nothing special going on there. Let’s hit F5 and see what happens:

astoriaaddition12.JPG

There is our CellPhone entity, fully exposed. There is one slight caveat to this, that helps us see one more requirement of our data container object: it needs to have a public empty constructor. If we tried to query the CellPhones collection in the above service we’d get an exception. So I’ll just modify my constructor to make it public, and viola, it works perfectly. The real power comes from the fact that I now can page, sort, and filter my cell phone data through this service without having to write a single line of code. That last sentence makes my head want to explode.

ADO.NET Data Services are so flexible it’s not even funny. It amazes me every time I think about it how applicable and powerful a data service could be in so many situations. If you break it down, a data service only has three basic pieces to it, each of which have very simply requirements:

  1. Entity - Must have an identifying property that matches one of three criteria
  2. Entity Container - Must contain public properties for each exposed entity type, that returns the entity collection as an IQueryable<T> (or derivative). Must also have a public empty constructor
  3. Service - Must specify the entity container it abstracts, and configure the access rights

That’s it. Those requirements are by no means limiting. Let’s take a look at the three pieces again and see what functionality is left wide open to us:

  1. Entity - Can contain any number of properties, methods, relationships, etc
  2. Entity Container - Can expose any number of entities, and can initialize that entity data from whatever data store it wants (not just relational)
  3. Service - Can expose any container it wants and apply any number of appropriate access right its wants

The service can actually provide more flexibility for exposing your data by means of service operations and interceptors, which we’ll cover in later posts.

So what is the moral of this long yet hopefully informative story? Basically that ADO.NET Data Services are freakin awesome. The sky is really the limit as to what you can accomplish with a data service. As we’ve seen, the service is in no way coupled to any specific data store or technology. We get interaction with LINQ To SQL and the Entity Framework for free, but that is a byproduct of their underlying compliance to the above requirements. The data service was by and large created to work with data in its most basic form and make manipulating and exposing it over the web very streamlined.

2 - Data service flavors

When a data service has its data source specified, that data source is used to determine which provider object is used by the service. That provider is what the data service uses as a bridge between it and the source. This fact is what allows such a flexible and powerful usage of data services. The provider is what governs how the data is examined, queried, and modified from the underlying source. It also governs how some of the data service’s behavior is determined. What does that mean? Well, depending on which data source you use with your data service, you might have to abide by certain conventions, and you might receive or lose certain behavior.

ADO.NET Data Services providers are simply classes that implement the IWebDataServiceProvider interface, or more specifically, inherit from the BaseServiceProvider class, which implements the aforementioned interface, and provides some base functionality. Out of the box, ADO.NET Data Services come with two providers: ReflectionServiceProvider, and ObjectContextServiceProvider. ObjectContextServiceProvider is used exclusively when your data service is using an EDM as its data source, or more specifically, an ObjectContext. ReflectionServiceProvider is used for everything else, which includes both LINQ To SQL, and custom IQueryable<T> implementations. What does that mean? It means that the rules for exposing an EDM might be different than any other source. An example is the identifier criteria that was mentioned in the previous post. The convention for specifying an identifier property of an entity class is different when using the Entity Framework as opposed to any other source.

This also means that LINQ To SQL itself is given no special treatment in terms of being exposed by a data service. As far as the data service is concerned, all it sees is an IQueryable<T> implementation, namely the Table<T> class. This point should drive home even further the fact that ADO.NET Data Services isn’t coupled to databases. Instead of looking at a data service that exposes a LINQ To SQL model and saying “Hey look! Data services work with databases!”, you can now say, “Hey look! Data services can understand the DataContext object hierarchy!”.

For the most part, the behavior/conventions used by each provider are the same. This is because a lot of the base provider behavior is provided by the BaseServiceProvider class that both ReflectionServiceProvider and ObjectContextServiceProvider inherit from (say that sentence 10 times fast). An example of this is the discovery of service operations in a service. This functionality is defined within BaseServiceProvider, and therefore works the same way regardless of the concrete provider being used. But, there are a few things that differ between the provider implementations. When a difference is noteworthy, it will be mentioned in the respective article.

Now that we have a grasp on the underlying mechanics of a data service, we can really begin to appreciate its additional features moving forward. The next post will go into detail on how you can perform advanced queries RESTfully on our data service, using completely out of the box functionality.


0 Responses to “ADO.NET Data Services Part 1: Services (contd.)”


  1. No Comments

Leave a Reply