Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with TPH Inheritance mapping in EF 4.1 with Code First

I have these classes in my ASP.NET MVC app:

public abstract class Person {
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Student : Person {
    public DateTime RegisteredOnUtc { get; set; }
    public int Age { get; set; }
}

public class Teacher : Person {
    public string LessonName { get; set; }
}

public class PersonMap : EntityTypeConfiguration<Person> {
    public PersonMap()
        : base() {

        this.HasKey(t => t.PersonId);

        this.Property(t => t.FirstName)
            .IsRequired()
            .HasMaxLength(50);

        this.Property(t => t.LastName)
            .IsRequired()
            .HasMaxLength(50);

        this.ToTable("Persons");

        this.Property(t => t.PersonId).HasColumnName("PersonId");
        this.Property(t => t.FirstName).HasColumnName("FirstName");
        this.Property(t => t.LastName).HasColumnName("LastName");

        this.Map<Student>(x => x.Requires("IsStudent").HasValue(true));
        this.Map<Teacher>(x => x.Requires("IsStudent").HasValue(false));
    }
}

public class StudentMap : EntityTypeConfiguration<Student> {
    public StudentMap()
        : base() {

        this.HasKey(t => t.PersonId); // Is this need or not???

        this.ToTable("Persons"); // Is this need or not???

        this.Property(t => t.RegisteredOnUtc).HasColumnName("RegisteredOn");
    }
}

public class TeacherMap : EntityTypeConfiguration<Teacher> {
    public TeacherMap()
        : base() {

        this.HasKey(t => t.PersonId); // Is this need or not???

        this.ToTable("Persons"); // Is this need or not???

        this.Property(t => t.LessonName)
            .IsRequired()
            .HasMaxLength(50);

        this.Property(t => t.LessonName).HasColumnName("Lesson");
    }
}

public class PersonContext : DbContext {

    public ObjectContext ObjectContext {
        get {
            return ((IObjectContextAdapter)this).ObjectContext;
        }
    }

    public DbSet<Person> Persons { get; set; }
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new PersonMap());
        modelBuilder.Configurations.Add(new StudentMap());
        modelBuilder.Configurations.Add(new TeacherMap());
    }

    public void Detach(object entity) {
        var objectContext = ((IObjectContextAdapter)this).ObjectContext;
        objectContext.Detach(entity);
    }
}

But, when I run the app, get this error:

The property 'PersonId' is not a declared property on type 'Student'. Verify that the property has not been explicitly excluded from the model by using the Ignore method or NotMappedAttribute data annotation. Make sure that it is a valid primitive property.

And if remove this.HasKey(t => t.PersonId); from the Student and Teacher, this error will be thrown:

The given key was not present in the dictionary.

Have you any idea to resolve this please? Thanks.

like image 885
amiry jd Avatar asked Aug 13 '26 21:08

amiry jd


2 Answers

Did you try removing both of these lines from StudentMap and TeacherMap?

this.HasKey(t => t.PersonId); // Is this need or not???

this.ToTable("Persons"); // Is this need or not???

I copied your code and ran it without these lines and it worked just fine.

like image 63
John Allers Avatar answered Aug 15 '26 10:08

John Allers


I little rewrite your code, it takes what you want:

You have the PersonId as primary key and all other limits are present.

public abstract class Person
{
    public int PersonId { get; set; }

    [Required]
    [MaxLength(50)]
    public string FirstName { get; set; }

    [Required]
    [MaxLength(50)]
    public string LastName { get; set; }
}

public class Student : Person
{
    [Column("RegisteredOn")]
    public DateTime RegisteredOnUtc { get; set; }
    public int Age { get; set; }
}

public class Teacher : Person
{
    [Required]
    [MaxLength(50)]
    public string LessonName { get; set; }
}

public class PersonMap : EntityTypeConfiguration<Person>
{
    public PersonMap()
    {
        Map<Student>(x => x.Requires("IsStudent").HasValue(true));
        Map<Teacher>(x => x.Requires("IsStudent").HasValue(false));
    }
}

public class PersonContext : DbContext
{
    public DbSet<Person> Persons { get; set; }
    public DbSet<Student> Students { get; set; }
    public DbSet<Teacher> Teachers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new PersonMap());
    }

    public void Detach(object entity)
    {
        var objectContext = ((IObjectContextAdapter)this).ObjectContext;
        objectContext.Detach(entity);
    }
}

Your DB looks like:

enter image description here

add code for test

    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Data.Entity.ModelConfiguration;
    using System.Data.Objects;

    namespace ConsoleApplication5
    {
        public abstract class Person
        {
            public int PersonId { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        public class Student : Person
        {
            public DateTime RegisteredOnUtc { get; set; }
            public int Age { get; set; }
        }

        public class Teacher : Person
        {
            public string LessonName { get; set; }
        }

        public class PersonMap : EntityTypeConfiguration<Person>
        {
            public PersonMap()
                : base()
            {

                this.HasKey(t => t.PersonId);

                this.Property(t => t.FirstName)
                    .IsRequired()
                    .HasMaxLength(50);

                this.Property(t => t.LastName)
                    .IsRequired()
                    .HasMaxLength(50);

                this.ToTable("Persons");

                this.Property(t => t.PersonId).HasColumnName("PersonId");
                this.Property(t => t.FirstName).HasColumnName("FirstName");
                this.Property(t => t.LastName).HasColumnName("LastName");

                this.Map<Student>(x => x.Requires("IsStudent").HasValue(true));
                this.Map<Teacher>(x => x.Requires("IsStudent").HasValue(false));
            }
        }

        public class StudentMap : EntityTypeConfiguration<Student>
        {
            public StudentMap()
                : base()
            {

                this.Property(t => t.RegisteredOnUtc).HasColumnName("RegisteredOn");
            }
        }

        public class TeacherMap : EntityTypeConfiguration<Teacher>
        {
            public TeacherMap()
                : base()
            {

                this.Property(t => t.LessonName)
                    .IsRequired()
                    .HasMaxLength(50);

                this.Property(t => t.LessonName).HasColumnName("Lesson");
            }
        }

        public class PersonContext : DbContext
        {

            public ObjectContext ObjectContext
            {
                get
                {
                    return ((IObjectContextAdapter)this).ObjectContext;
                }
            }

            public DbSet<Person> Persons { get; set; }
            public DbSet<Student> Students { get; set; }
            public DbSet<Teacher> Teachers { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
                modelBuilder.Configurations.Add(new PersonMap());
                modelBuilder.Configurations.Add(new StudentMap());
                modelBuilder.Configurations.Add(new TeacherMap());
            }

            public void Detach(object entity)
            {
                var objectContext = ((IObjectContextAdapter)this).ObjectContext;
                objectContext.Detach(entity);
            }
        }

        public class Program
        {
            static void Main()
            {
                var personContext = new PersonContext();
                personContext.Database.Delete();
                personContext.Database.Create();
            }
        }
    }
like image 25
Alexander Molodih Avatar answered Aug 15 '26 11:08

Alexander Molodih