Introduction
This is the Entity Framework article series. In our previous two articles we learned various approaches to working with Entity Framework and various strategies to handle a database in a code first approach. You can read it here:
In this article we will learn to tackle inheritance in Entity Framework. If you are not clear on the aim of the article, the following should help explain it for you:
We know that inheritance is a beautiful concept in Object Oriented programming and it provides the concept of re-usability, which is one of the most important practices of software design principal. OK, so we will implement inheritance at the class level, that’s fine, but how will we implement it at the DB level? As a rule, we know the derive class contains all the properties of the base class, so will the derive class table contain all the properties of the base class table?
We will learn that stuff in this article along with next two articles. There are three approaches in Entity Framework level to tackle inheritance in the code level.
- Table per hierarchy
- Type per hierarchy
- Table per concrete Type
In this article we will understand Table per Hierarchy. We know that inheritance is nothing but a hierarchical concept, where one class is derived from another class. It forms a tree structure by classes and subclasses. The table per hierarchy concept says that, one table will get create per hierarchy. For example, if there is class A, and class B is derived from class A, then for that hierarchy only one table will get created in the database. So the basic concept is for a single hierarchy there will be one table. Have a look at the below code.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace CodeFirst
{
public class Person
{
[Key]
public int PersonId { get; set; }
[Required]
[MaxLength(10)]
public string Name { get; set; }
public string surname { get; set; }
}
public enum FriendType
{
Close,
NotClose,
FamilyFriend
}
public class Friend : Person
{
public Int32 Person { get; set; }
public FriendType FriendType { get; set; }
}
public class TestContext : DbContext
{
public TestContext()
: base("DBConnectionString")
{
}
public DbSet<Person> Person { get; set; }
}
}
In this example, we have implemented one hierarchy by inheriting the Person
class within the Friend
class. Entity Framework is smart enough to implement Table per Hierarchy without any external instructions.
Now let's have a look at the TestContext
class. We are passing the connection string through the constructor and the connection string needs to be configured in the web.config file. Within the class, we are initiating only the Person
class, not the Friend
class. This is the beauty of Entity Framework. If we populate the base class then the related derive class will also automatically configure in a single table. Here is the connection string in the web.config file for this example.
<connectionStrings>
<add name="DBConnectionString"
connectionString="Data Source=SERVERNAME;Initial Catalog=PersonDB;Integrated Security=true"
providerName="System.Data.SqlClient"/>
</connectionStrings>
Once we run the application, we will see that the database is created and table has generated as shown below.
If we closely look on table stricture we will see one Discriminator column. Why was the extra column created? We did not implement it in the code. Entity Framework has smartly created that field to distinguish between tables. The concept is like this: both the Person
object and the Friend
object will save in a single table, so how we will distinguish them? How will we know whether the particular row containing data is for the Person
object or the Friend
object. To distinguish between them, the Discriminator column has introduced. Here is the code where we are saving the object of Person
class:
static void Main(string[] args)
{
using (var ctx = new TestContext())
{
ctx.Person.Add(new Person { Name = "Sourav", surname = "Kayal" });
ctx.SaveChanges();
}
}
Have a look on table data, we are seeing that "Person" has saved in Discriminator column value, so the record contains object of the Person
class.
Now, we will save the object of the Friend
class in the same table. Have a look at the below code:
static void Main(string[] args)
{
using (var ctx = new TestContext())
{
ctx.Person.Add(new Friend { Name = "Foo", surname = "Bar" ,PersonId = 1,FriendType =FriendType.Close});
ctx.SaveChanges();
}
}
Here the data was saved in the table and the value for the Discriminator column is Friend.
Customize the "Discriminator" column
You may wonder: is it possible to change the default "Discriminator" column name? Or change it’s value? Yes, it’s possible. Using Fluent API we can do it. Have a look at the modified code.
public class TestContext : DbContext
{
public TestContext()
: base("DBConnectionString")
{
}
public DbSet<Person> Person { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Map<Friend>(m => m.Requires("ObjectType").HasValue("FriendType"));
modelBuilder.Entity<Person>()
.Map<Person>(m => m.Requires("ObjectType").HasValue("PersonType"));
}
}
Now, when we will save the data of some Object. For example, the Friend
Object will save the value "Friend Type," unlike "Friend" in the previous example. Let’s try to save data using the code shown below.
static void Main(string[] args)
{
using (var ctx = new TestContext())
{
ctx.Person.Add(new Friend { Name = "Foo", surname = "Bar" ,PersonId = 1,FriendType =FriendType.Close});
ctx.SaveChanges();
}
}
Have a look at the database structure. We are seeing that "FriendType
" has saved in table because we have save object of Friend
class.
Now, we will try to save the object of Person
class in a table. Here is sample code:
static void Main(string[] args)
{
using (var ctx = new TestContext())
{
ctx.Person.Add(new Person { Name = "Ram", surname = "Kumar" });
ctx.SaveChanges();
}
}
Now, the value is "Person Type" in table.
So we have learned the concept of table creation and the concept of the Discriminator column, now we will learn to fetch data from the table. To fetch data from such a table, we can use both a polymorphic and a non-polymorphic query. Just try both of them:
Polymorphic – Query
As the concept, it will pull all related hierarchical data in single query. Here we are pulling all data related to the Person
Object. Now since Friend
is derived from Person
, Friend's information is pulling automatically. Here is the sample code.
using (var ctx = new TestContext())
{
var data = from tab in ctx.Person select tab;
List<Person> persons= data.ToList();
}
See that both rowse have been fetched from the table so we are getting both the Friend
and Person
object.
Non-Polymorphic Query
In this query we will pull a particular column value. Here we are pulling the "Friend
" object only.
using (var ctx = new TestContext())
{
var data = from tab in ctx.Person.OfType<Friend>() select tab;
List<Friend> persons = data.ToList();
}
And we are getting only a single object.
Border line
We have learned to implement the Table per Hierarchy concept to implement inheritance in Entity Framework. Hopefully this article will help you to go ahead one step towards Entity Framework learning. In the coming articles we will understand the two additional inheritance strategies in the Entity Framework. environment.