ADO.NET Data Services provides a robust REST API over top of a data source. That data source could be 1) the Entity Framework (EF), 2) LINQ to SQL, or 3) your own custom data source that implements IQueryable and/or IUpdatable. However, it should be noted that in v1, EF is really the "first class" data source for ADO.NET Data Services because it supports both IQueryable and IUpdatable out of the box. In this post I'll discuss the scenario where you have inheritance in your entity object model and the pros and cons of the implementation. As a stand-alone ORM tool, EF has not always been extremely well received by the developer community especially in comparison to other tools such as LINQ to SQL and NHibernate. While I generally share many of the frustrations of my fellow developers with EF, the 2 reasons I find it compelling enough to investigate further are: 1) it's first class support for ADO.NET Data Services (and data services is great), and 2) the enhancements that are coming in EF vNext including Code Only with POCO which will bring EF much more in line with other ORM tools that already enable these POCO scenarios as well as Model First development.
EF supports 3 types of inheritance: 1) Table per Hierarchy (aka Single Table Inheritance), 2) Table per Type, and 3) Table per Concrete Type. In this example, I'll be using the Table per Type (TPT) inheritance. I won't cover every detail as to how I set up the TPT inheritance with EF but if you haven't done it before then you should check out this post here that covers step by step how to do it. In my scenario, suppose I have an EF model that looks like this:
Notice that I have a base class of Client which both Person and Business inherit from. Additionally, a Person and a Business will have ClientAddresses (they inherit the addresses from the base Client class). I add a new ADO.NET Data Service to my project which looks like this:
public class ClientDataService : DataService<ClientEntities> { public static void InitializeService(IDataServiceConfiguration config) { config.SetEntitySetAccessRule("*", EntitySetRights.All); } }
Note that I named my EF object context "ClientEntities" so the DataService can be strongly-typed as shown above. Also note that I'm completely opening up my permissions to all entities by using the * wildcard in the SetEntitySetAccessRule() call above but in production you'd want to think through your permissions a little more than shown in my demo code above.
Now at this point, if we fire up our data service we'll see the default meta-data screen:
This screen shows the entity sets I have available to query but interestingly it does not show that I have a Person or Business entity set available (more on that later). So how do I get a Person or a Business object? I just so happen to know that in my database, the ClientID of 1 is a Person and the ClientID of 2 is a Business. If I do a query for a Client with these respective ClientID's, it actually figures it out for you.
Here is my query for a Client who is a Person:
And here is my query for Client who is a Business:
Data Services in conjunction with EF will correctly instantiate the correct concrete type. In one regard, this is great. But on the other hand, this isn't very explicit. The URI I used was: /Clients(2). Wouldn't it have been more explicit if I could have used the URI of /Persons(2)? After all, that is the name of my EntitySet. But actually it's not the name of my EntitySet. In these inheritance situations, EF allows you to specify the Entity Set Name for the base type, but it does not allow you the set the Entity Set Name for the sub-class – that just takes the same Entity Set Name as it's base class. One way for us to use a more intuitive URI would be to create a Service Operation like this:
[WebGet] public IQueryable<Person> Persons() { var results = from c in this.CurrentDataSource.Clients where c.ClientType == "PERSON" select c; return results.ToList().Cast<Person>().AsQueryable(); }
Note you must also enable permissions to this new service operation by adding this line of code your your InitializeService() method:
config.SetServiceOperationAccessRule("Persons", ServiceOperationRights.All);
So now you are able to get a Person object with a more explicit and "strongly-typed" URI like this:
This is all well and good but it does have some drawbacks. For one thing, this service operation does not appear on the default page that shows all the meta data for the data service so the discoverability for something like this is not great. Additionally, on the client side, the consuming developer will also have to understand these implementation details and handle the casting on their end if they're consuming data services in the default fashion with the data services proxy. Therefore, while I've found this scenario to be possible with Data Services and EF, I don't really put it in the category of extremely user friendly. However, these drawbacks seem to be more of a function of the implementation of EF than ADO.NET Data Services so I'm still a fan of data services. It will be interesting to see how the enhancements in EF 4.0 (especially with the renewed emphasis on POCO) will change the game with scenarios like these.