I am looking to implement Table-per-Hierarchy using EF6 similar to the instructions found here: example.
I have an abstract base class of User with the following derived types:
When I examine the database table Users the discriminator column value is (Undefined) when I pass a student object into my Save method below. Instead I would expect the value to be Student. Otherwise my data is saved correctly in both the Users and Students tables.
While troubleshooting the problem I added a UserType enumerator Get property to the classes to ensure that I am casting from User to Student.
In my UserRepository class my Save method is below.
public void Save(User user)
{
if (Exists(user.Id))
UpdateUser(user);
else
{
switch (user.Role)
{
case UserType.Role.Base:
_db.Users.Add(user);
break;
case UserType.Role.Student:
_db.Users.Add(user as Student);
break;
case UserType.Role.Instructor:
_db.Users.Add(user as Instructor);
break;
case UserType.Role.Contact:
_db.Users.Add(user as Contact);
break;
}
}
_db.SaveChanges();
}
I've tried code like the following to explicitly create a new Student.
private void MapToStudent(User user)
{
_db.Users.Add(new Student()
{
FirstName = user.FirstName,
LastName = user.LastName,
//...
});
}
I am not downcasting correctly? Or rather what is the proper/preferred way to save subclasses using EF?
public abstract class User
{
public int Id { get; set; }
//...
}
internal class UserNotFound: User
{
public override UserType.Role Role
{
get
{
return UserType.Role.Base;
}
}
}
public class Student : User
{
//...
public override UserType.Role Role
{
get { return UserType.Role.Student; }
}
}
public class Contact : User
{
//...
public override UserType.Role Role
{
get { return UserType.Role.Contact; }
}
}
public class Instructor : User
{
//...
public override UserType.Role Role
{
get { return UserType.Role.Instructor; }
}
}
public class DatabaseContext : Context
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("Students");
modelBuilder.Entity<Contact>().ToTable("Contacts");
modelBuilder.Entity<Instructor>().ToTable("Instructors");
}
}
It appears your mappings are incorrect for TPH. The linked example in your questions shows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BillingDetail>()
.Map<BankAccount>(m => m.Requires("BillingDetailType").HasValue("BA"))
.Map<CreditCard>(m => m.Requires("BillingDetailType").HasValue("CC"));
}
which modeled after your question might look like:
modelBuilder.Entity<User>()
.Map<Student>(m => m.Requires("Discriminator").HasValue("STU"))
.Map<Instructor>(m => m.Requires("Discriminator").HasValue("INS"));