Entity Framework Mapping Inheritance – Table Per Hierarchy

In Entity Framework, Table Per Hierarchy (TPT) mapping inheritance is a way to express closely related entities stored in a single table. We might store blue collar workers, middle managers and sales persons in a single Employee table, but represent them in our model as ‘distinct’ entities.

In this example we have our Music model with an Artist table filled with all kinds of bands, performers, singer-songwriters, studio projects and so on. We need to query for the singer-songwriters type every so often by using the IsSingerSongwriter bit.

Entity Framework Artist Model

With Entity Framework we can inherit from Artist to create a special purpose SingerSongwriter entity so we don’t need to specify the IsSingerSongwriter property every time. We do this by creating a new entity SingerSongwriter, with a base type Artist.

Entity Framework SingerSongwriter Model

We do the same for the rest of the Artist rows and assume if IsSingerSongwriter is false that these are all Bands. The SingerSongwriter and Band entities inherit all properties from Artist.

Entity Framework Inheritance SingerSongwriter

Edit the properties of the Artist entity and set the Abstract property to true.

Entity Framework Artist Abstract Entity

We need to set mapping conditions for both derived entities.

Entity Framework Mapping Details SingerSongwriter

And as a last step we need to remove the IsSingerSongwriter property from our Artist base class (since we can’t map a property twice).

Entity Framework Artist Base Mode

To demonstrate the use of the Artist base entity we have a (rather ugly and dependent) test that will pass only when both Ane Brun (a great singer-songwriter) and Genesis (the greatest pop band ever when Peter Gabriel was still a member) are returned. We also wrote a test where we query only the SingerSongwriter entity. This test will pass only when Ane Brun is present and Genesis is not.

using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Diagnostics;
using System.Linq;
using Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace MusicEntitiesTests
{
    [TestClass]
    public class ArtistTest
    {
        [TestMethod]
        public void SelectFromArtistReturnsAllArtistTypes()
        {
            using (var context = new MusicModelContainer())
            {
                ObjectSet<Artist> query = context.Artists;
                List<Artist> artistDump = query.ToList();
 
                WriteSqlToDebug(query);
                WriteArtistsToDebug(artistDump);
 
                // Has singer-songwriters
                Assert.IsTrue(artistDump.Any(a => a.Name.Equals("Ane Brun")));
                // Has bands
                Assert.IsTrue(artistDump.Any(a => a.Name.Equals("Genesis")));
            }
        }
 
        [TestMethod]
        public void SelectFromSingerSongwriterReturnsThisSingerSongwriterOnly()
        {
            using (var context = new MusicModelContainer())
            {
                ObjectQuery<SingerSongwriter> query =
                    context.Artists.OfType<SingerSongwriter>();
                List<SingerSongwriter> artistDump = query.ToList();
 
                WriteSqlToDebug(query);
                WriteArtistsToDebug(artistDump);
 
                // Has singer-songwriters
                Assert.IsTrue(artistDump.Any(a => a.Name.Equals("Ane Brun")));
                // Has no bands
                Assert.IsFalse(artistDump.Any(a => a.Name.Equals("Genesis")));
            }
        }
 
        private static void WriteSqlToDebug(object query)
        {
            Debug.WriteLine(String.Empty);
            Debug.WriteLine(((ObjectQuery) query).ToTraceString());
            Debug.WriteLine(String.Empty);
        }
 
        private static void WriteArtistsToDebug(object artistDump)
        {
            foreach (Artist artist in (IEnumerable<Artist>) artistDump)
            {
                Debug.WriteLine(artist.Name);
            }
        }
    }
}

Both tests pass. The following debug output is for the Artist query. Entity Framework doesn’t filter the result and returns all artists.

Unit Test Select From Artist

The debug output shown below is for the SingerSongwriter test. Entity Framework automatically filtered out the singer-songwriters for us. Even if we create a new SingerSongwriter entity and attach it to the datacontext it will automatically set the IsSingerSongwriter bit to true on the Artist table when inserted into the database.

Unit Tes tSelect From SingerSongwriter

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>