Configuring Table Names
By default, Code First conventions dictate that an entity property name in the DbContext will be pluralized to form the table name. To override this convention, the TableAttribute from the System.ComponentModel.DataAnnotations.Schema namespace can be applied to the model class.
[Table("ApplicationUsers")]
public class User
{
// Entity properties
}
Mapping Inheritance Hierarchies
Object-oriented programming relies heavily on inheritance, and Entity Framework supports several strategies to map inheritance structures to relational databases.
1. Table Per Hierarchy (TPH)
TPH is the default strategy in Code First. It maps an entire inheritance hierarchy to a single database table. A special column, called a discriminator, is used to identify the specific type for each row.
Consider a base class Person and a derived class Student:
public enum Gender { Male, Female }
public class Person
{
[Key]
public string Id { get; set; }
public string FullName { get; set; }
public Gender Sex { get; set; }
public int Age { get; set; }
}
public class Student : Person
{
public string EnrollmentNo { get; set; }
public string Major { get; set; }
}
When adding these entities to the context, EF creates a single table People containing columns for both classes. A Discriminator column (nvarchar) is automatically added to distinguish between Person and Student rows.
You can customize the discriminator column name and values using the Fluent API in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Map(m => m.Requires("UserType").HasValue("Base"))
.Map<Student>(m => m.Requires("UserType").HasValue("Learner"));
}
2. Table Per Type (TPT)
TPT creates a separate table for each type in the hierarchy. The table for the derived class contains only the properties specific to that class and a foreign key linking back to the base class table.
To implement TPT, simply use the [Table] attribute on the derived class:
[Table("Students")]
public class Student : Person
{
public string EnrollmentNo { get; set; }
public string Major { get; set; }
}
This results in two tables: People (containing common fields) and Students (containing specific fields and a primary key that is also a foreign key to People).
3. Table Per Concrete Type (TPC)
TPC maps each concrete class to its own table. Unlike TPT, the derived class table contains all properties, including those inherited from the base class. This strategy requires Fluent API configuration.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Map(m => { m.ToTable("People"); })
.Map<Student>(m =>
{
m.ToTable("Students");
m.MapInheritedProperties();
});
}
Mapping Multiple Entities to One Table (Entity Splitting)
Sometimes, its necessary to map two entities that share a primary key to a single table. This is useful for splitting a large table into more manageable domain objects (e.g., separating security credentials from user profile data).
Consider a User entity and a UserProfile entity that both map to the Users table. They must share the same primary key.
[Table("Users")]
public class User
{
[Key, ForeignKey("Profile")]
public string UserId { get; set; }
public string Username { get; set; }
public UserProfile Profile { get; set; }
}
[Table("Users")]
public class UserProfile
{
[Key, ForeignKey("Account")]
public string UserId { get; set; }
public DateTime DateOfBirth { get; set; }
public User Account { get; set; }
}
In this configuration, adding a User and a UserProfile linked by the same ID will populate a single row in the database. Both entities require the [Table] attribute pointing to the same table name, and navigation properties must be defined to establish the one-to-one relationship.
Mapping One Entity to Multiple Tables (Table Splitting)
Converse, a single entity can be split across multiple database tables. This allows for optimizing schema design by moving heavy or less frequently used columns to a separate table.
Using the Student entity as an example, we can map basic information to a Person table and academic details to a StudentInfo table:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.Map(m =>
{
m.Properties(p => new { p.Id, p.FullName, p.Age });
m.ToTable("Person");
})
.Map(m =>
{
m.Properties(p => new { p.Id, p.EnrollmentNo, p.Major });
m.ToTable("StudentInfo");
});
}
When saving a Student object, Entity Framework will distribute the data into the two specified tables based on the property mappings.