Posts
203
Comments
1116
Trackbacks
51
June 2009 Entries
Inheritance with ADO.NET Data Services and the Entity Framework

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:

   1:  public class ClientDataService : DataService<ClientEntities>
   2:  {
   3:      public static void InitializeService(IDataServiceConfiguration config)
   4:      {
   5:          config.SetEntitySetAccessRule("*", EntitySetRights.All);
   6:      }
   7:  }

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:

   1:  [WebGet]
   2:  public IQueryable<Person> Persons()
   3:  {
   4:      var results = from c in this.CurrentDataSource.Clients
   5:                    where c.ClientType == "PERSON"
   6:                    select c;
   7:      return results.ToList().Cast<Person>().AsQueryable();
   8:  }

Note you must also enable permissions to this new service operation by adding this line of code your your InitializeService() method:

   1:  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.

Posted On Thursday, June 25, 2009 9:29 PM | Comments (8)
MVC HTML Helper – SubmitLink

Often when creating web applications, it’s common for us to want to submit or post forms to the server by using a hyperlink rather than an HTML submit button.  It might be visually more appealing/consistent or whatever your reason might be to have your buttons look like this:

The Cancel button is easy because we can just use a normal Hmtl.ActionLink helper to redirect to whatever our cancel page is.  But what to do for the Save link given that we don’t have anything in MVC out of the box that is analogous to the LinkButton in ASP.NET web forms?  One solution might be to just use JavaScript like this:

   1:  <a href="javascript:document.mainForm.submit();">Save</a>
   2:  <%=Html.ActionLink("Cancel", "Index") %>

But that is somewhat ugly and the paradigm is inconsistent with the Cancel button where we’re using a normal ActionLink.  An alternative for this scenario is to just create a simple SubmitLink HTML helper that will allow you’re code to look like this:

   1:  <%=Html.SubmitLink("Save", "mainForm") %>
   2:  <%=Html.ActionLink("Cancel", "Index") %>

You can see this allows us to have a consistent coding paradigm.  The first argument is the link text and the second is the form name.  The implementation looks like this:

   1:  public static string SubmitLink(this HtmlHelper htmlHelper, string linkText, string formName, object htmlAttributes)
   2:  {
   3:      TagBuilder tagBuilder = new TagBuilder("a");
   4:      tagBuilder.MergeAttribute("href", string.Format("javascript:document.{0}.submit();", formName));
   5:      tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
   6:      tagBuilder.InnerHtml = linkText;
   7:   
   8:      return tagBuilder.ToString();
   9:  }
  10:   
  11:  public static string SubmitLink(this HtmlHelper htmlHelper, string linkText, string formName)
  12:  {
  13:      return htmlHelper.SubmitLink(linkText, formName, null);
  14:  }

Notice there is an overload to takes htmlAttributes so that you can apply any arbitrary attributes you might want.  For example, you might want to apply a CSS class that looks like this:

   1:  <%=Html.SubmitLink("Save", "mainForm", new { @class = "foo" }) %>

These types of little HTML helpers are so easy to build with MVC, especially given that we can use the TagBuilder class, that it really enables a lot of re-use across your applications.

Posted On Wednesday, June 3, 2009 1:56 PM | Comments (7)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud