使用Code First在EF 4.1中进行TPH继承映射的问题

时间:2011-09-18 16:41:50

标签: c# entity-framework mapping entity-framework-4.1 ef-code-first

我在ASP.NET MVC应用程序中有这些类:

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);
    }
}

但是,当我运行应用程序时,会收到此错误:

属性'PersonId'不是'Student'类型的声明属性。使用Ignore方法或NotMappedAttribute数据注释验证是否未从模型中明确排除该属性。确保它是有效的原始属性。

如果从this.HasKey(t => t.PersonId);Student移除 Teacher,则会抛出此错误:

字典中没有给定的密钥。

你有任何想法解决这个问题吗?感谢。

3 个答案:

答案 0 :(得分:1)

我很少重写你的代码,它需要你想要的东西:

您将PersonId作为主键 并且存在所有其他限制。

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);
    }
}

您的数据库看起来像:

enter image description here

添加测试代码

    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();
            }
        }
    }

答案 1 :(得分:1)

您是否尝试从StudentMap和TeacherMap中删除这两行?

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

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

我复制了你的代码并在没有这些行的情况下运行它并且它运行得很好。

答案 2 :(得分:1)

如果您在PersonContext类中包含Student和Teachers,我认为它不是TPH继承。 TPH的全部要点就是只有一个表,Person表?我会删除这两行

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

如果您查看this walkthrough,特别是“将人员实体类型添加到模型”部分,您将看到我的意思。我不确定这是否与您的特定问题有很大关系,但无论如何您可能还想注意它。