Inheritance and the Entity Framework

Tuesday, December 18, 2007

Here is an excerpt of the schema for the top level of OdeToCode:

The Community_ContentPages table carries all of the data needed by every type of content – like moderation flags, published date, and owner ID. Community_Articles carries just the extra information needed for a published article – like the article text itself.

Inside the code, Article and Book classes derive from ContentPage. Inheritance simplifies the implementation, because a great deal of the business logic applies to all the different types of content objects. For instance, the moderation and approval rules apply to articles, book reviews, comments, images, and other entities that all derive from ContentPage.

Mapping to Objects

Inheritance mapping is one good litmus test for the capabilities of an ORM product. LINQ to SQL, for example, only handles one of the three common strategies for modeling inheritance in an RDBMS – the "table per class hierarchy" strategy, which doesn't help me in this scenario. Most full featured OR/M products, like NHibernate, support multiple strategies, including the "table per subclass" strategy I need.

The Entity Framework documentation includes some pointers on table per subclass mapping (known as "table per type" or TPT in EF terminology). Although the EF designer is easy for getting tables and columns spit out into a mapping file, I found working with the designer a bit tedious. Trying to model inheritance using the designer created build errors, so I did most of the XML editing by hand. The key to TPT mapping is:

  • Use a BaseType attribute when defining EntityTypes in the conceptual schema.
  • Define a single Key column only on the base EntityType in the conceptual schema.
  • In the mapping specification, combine the EntityTypeMapping tags for all derived types into a single EntitySet:
<EntityContainerMapping StorageEntityContainer="dbo"
          
CdmEntityContainer="OdeToCodeEntityContext">
  <EntitySetMapping Name="ContentPages">
    <EntityTypeMapping
          
TypeName="IsTypeOf(OdeToCode.Core.Entities.ContentPage)">
      ...
    
</EntityTypeMapping>
    <EntityTypeMapping
          
TypeName="IsTypeOf(OdeToCode.Core.Entities.Article)">
      ...
    
</EntityTypeMapping>
  </EntitySetMapping>
</
EntityContainerMapping>


Querying

Once the XML work is done, it's relatively easy to pull out persisted entities.

Given the following DTO:

public class ArticleSummary
{
    
public int ID { get; set; }
    
public string Title { get; set; }
}

I can use the following LINQ query to pull out only Article objects. The trick is the OfType() operator:

IQueryable<ArticleSummary> articles =
   
from article in context.ContentPages.OfType<Article>()
   
select new ArticleSummary { ID = article.ID,
                                Title = article.contentPage_title };

Although I still have some reservations about EF, I'm warming up to the framework...

*If the naming convention strikes you as odd, then remember the schema was designed to work in a shared hosting environment where a single database might need to support multiple applications. The Community prefix helps to avoid naming collisions. An equivalent design today could use schemas in SQL 2005.


Comments
Catto Tuesday, January 8, 2008
Hey Now K Scott,
Nice Post, Very informative.
Thx 4 the Info,
Catto
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!