pr0g33k

Handling Inheritance Using Table-Per-Type in Entity Framework Code First

There are a few ways to handle persisting inheritance with Entity Framework 5 Code First. Table-Per-Hierarchy and Table-Per-Type are the most common. TPH seems to be the most prevalent but I'm not a huge fan. With TPH, only one table is created to handle the parent type and it's child types.

For example, suppose you had a class named "Person" and two inherited classes named "Instructor" and "Student". "Person" has two properties: "FirstName" and "LastName." "Instructor" as a property for "HireDate." "Student" has a property for "EnrollmentDate." When Entity Framework creates the database for this model, by default it creates a single table with columns for all of the class properties. "HireDate" and "EnrollmentDate" are nullable such that "Instructor" records would have a value for "HireDate" but "EnrollmentDate" would be null whereas "Student" records would have a value for "EnrollmentDate" but "HireDate" would be null. To distinguish among the different sub-types, there's a "Discriminator" column added to the table that holds the name of the sub-type (e.g. "Instructor" and "Student"). The "Discriminator" column denormalizes the data and would likely irritate any DBA's in your organization.

When you set up your classes to use Table-Per-Type, three tables are created: "Person," "Instructor," and "Student" with one-to-one relationships. You avoid the denormalization but, supposedly, there's a performance cost on the Entity Framework side because you have to join the tables when you query them. Personally, I'd rather avoid the denormalization - especially for tables that are expected to grow to any great number of rows.

Here are the classes:

public abstract class Person
{
    [Key]
    public Int32 Id { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

public class Instructor : Person
{
    public DateTime HireDate { get; set; }
}

public class Student : Person
{
    public DateTime EnrollmentDate { get; set; }
}
    

I prefer to use the Fluent API to specify how the tables are created as opposed to using attributes in the POCO classes. To get EF to create the separate tables, use the .ToTable(tableName) method:

public class UniversityContext : DbContext
{
    public DbSet<Person> People { set; get; }
    public DbSet<Instructor> Instructors { set; get; }
    public DbSet<Student> Students { set; get; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<Instructor>().ToTable("Instructor");
        modelBuilder.Entity<Student>().ToTable("Student");
    }
}
    
Posted on 9/2/2013 at 05:09 PM , Edited on 9/2/2013 at 05:09 PM

Comments:

Leave a comment
  1. CAPTCHA