.NET

Implementing Inheritance with Entity Framework Code First

1. Introduction

This walkthrough is an introduction to implementing inheritance with Entity Framework, Code First approach. A simple strategy for mapping database tables might be “one table for every persistent class”. This approach sounds simple enough and, indeed, works very well until I encountered inheritance. The requirement I was tasked with was simple, develop and implement a database for books. In the domain model, we have a Book base class (note the font on the UML class diagram below). We do allow for various book types and represent them as subclasses of Book class. As for now, we support Fiction and Non Fiction.

Books Domain Model
Books Domain Model

2. Pre-requisites

You will need to have Visual Studio installed any version from 2010 and above will suffice. If you are using 2010, you will also need to have NuGet installed.

 

3. Create the application

To keep things simple, we are going to build a console application that uses Code First to implement inheritance.

  1. Open Visual Studio
  2. File -> New -> Project
  3. Select Windows from the left menu and Console Application 
  4. Enter CodeFirstEFInheritanceExample as the name
  5. Select OK
New Console Application
New Console Application

4. Create the model

We shall define a very simple model using classes. EF enables us to use custom data classes together with the data model using “plain old” CLR Objects (POCO). These POCO classes shall be mapped to entities that are defined in the data model as shown below:

Book.cs

public abstract class Book
    {
        [Key]
        public int BookId { get; set; }
        public BookTypeEnum BookType { get; set; }
        public string Title { get; set; }
        public string ISBN { get; set; }
        public string Author { get; set; }
        public string Summary { get; set; }
        public enum BookTypeEnum : byte
        {
            Fiction,
            NonFiction
        }
        public override string ToString()
        {
            return Title + " By : " + Author;
        }
    }

FictionBook.cs

public sealed class FictionBook : Book
    {
        public override string ToString()
        {
            //Don't give away too much of the plot!)
            return string.Empty;
        }
    }

NonFictionBook.cs

public sealed class NonFictionBook : Book
    {
        public override string ToString()
        {
            return Summary.ToString();
        }
    }

Please note that we shall override the summary for Fiction books using the awesome power of polymorphism, since we don’t want to give away too much of the plot!
This entire class hierarchy can be mapped to a single table in SQL Server, the table will contain columns for the all the properties in the class hierarchy. The concrete subclasses will be identified by value of a type discriminator column. The Books table in SQL Server will be as shown below:
DbScript.sql

CREATE TABLE Books(
    BookId int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    BookType tinyint NOT NULL,/*0 Fiction, 1 Non Fiction  */
    Title varchar(50) NOT NULL, 
    ISBN varchar(50) NOT NULL,
    Author varchar(50) NOT NULL,
    Summary varchar(5000) NOT NULL,
    Discriminator int NOT NULL
    );

I’m sure you are wondering what the column discriminator is all about, well, Code First uses this column to distinguish between pertinent classes. This column as you noticed is not part of any property in the domain model, it’s used internally by EF Code First.

5. Create the context

Now, we need to define a derived context, which represents a session with the database, allowing us to query and save data. We define a context that derives from System.Data.Entity.DbContext and exposes a typed DbSet<TEntity> for each class in our model.

We’re now starting to use types from the Entity Framework so we need to add the EntityFramework NuGet package.

  1. Project -> Manage NuGet Packages…
  2. Note: If you don’t have the Manage NuGet Packages… option you should install the latest version of NuGet
  3. Select the Online tab
  4. Select the EntityFramework package
  5. Click Install

BooksDbContext.cs

public class BooksDbContext : DbContext
    {
        public BooksDbContext() : base("BooksConnectionString") { }
        public DbSet Books { get; set; }
        /*
        Table per Hierarchy (TPH): Enable polymorphism by denormalizing the SQL schema, and utilize a 
        type discriminator column that holds type information 
        
        */
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity()
                .Map(m => m.Requires("Discriminator").HasValue(0))
                        .Map(m => m.Requires("Discriminator").HasValue(1));
        }
    }

By default the column name is “Discriminator”, and its type is string, however, this can be overridden in the BooksDbContext class as show above. By overriding the OnModelCreating method, we can then specify the value that the Discriminator column will have in our table, in this case the Discriminator column will be of type int.

For the uninitiated, lack of the “Discriminator” column in the respective table will result in the following exception.
{“Invalid column name ‘Discriminator’.\r\nInvalid column name ‘Discriminator’.\r\nInvalid column name ‘Discriminator’.”}

 

6. Summary

In this post we learned how to implement inheritance using EF Code First approach in order to overcome the structural mismatch that exists between the object oriented and relational worlds.

Download the project
You can download the full source code of this example here : EFCodeFirstInheritance

Stephen Ebichondo

Stephen is an application developer currently working as a technical lead at Equity Bank’s in-house mobile applications team. Passionate about all matters tech and building user communities, he is the current lead of Nairobi .NET User Group.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button