无法在EF 6上使用Fluent API创建TPH



i建模类似于Google形式的项目。波纹管实体非常简单明了(我想),如下所示。

问题类型:

// Base class for any kind of question
public abstract class Question : Bean
{
    public string Statement { get; set; }
}
// Visual questions are questions where images are answers.
public class VisualQuestion : Question
{
    public virtual VisualAnswer Answer { get; set; }
}
// Discursive questions are questions where big texts are answers.
public class DiscursiveQuestion : Question
{
    public virtual DiscursiveAnswer Answer { get; set; }
}
// Objective questions are questions that can have multiple answers,
// where each of them should be no bigger than 1 character.
public class ObjectiveQuestion : Question
{
    public virtual List<ObjectiveQuestionOption> Options { get; set; }
}
// Options for objective questions.
public class ObjectiveQuestionOption : Question
{
    public int ObjectiveQuestionId { get; set; }
    public virtual ObjectiveQuestion Question { get; set; }
    public virtual ObjectiveAnswer Answer { get; set; }
}

答案类型:

public abstract class Answer : Bean
{
    public int QuestionId { get; set; }
}
public class DiscursiveAnswer : Answer
{
    public string Answer { get; set; }
    public virtual DiscursiveQuestion Question { get; set; }
}
public class ObjectiveAnswer : Answer
{
    public char Answer { get; set; }
    public virtual ObjectiveQuestion Question { get; set; }
}
public class VisualAnswer : Answer
{
    public byte[] Blob { get; set; } // Image answer
    public virtual VisualQuestion Question { get; set; }
}

bean是:

public abstract class Bean 
{
    public int Id { get; set; }
}

对于我可能有一个Question对象和一个QuestionOption的问题,对于客观问题。如果是这样,我们将需要Question中的所有3个Answer对象,这听起来不正确(一个人需要识别问题类型,然后像isas Casts相应地访问其答案成员)。对于解决方法,我决定将问题分为上面定义的3个对象,并使用TPH方法具有单独的答案成员。

所有情况似乎只有1个条件:所有流利的API设置必须在DbContext类的void OnModelCreating(DbModelBuilder modelBuilder)中完成(我已经覆盖了它)。这是一个问题,因为我正在为每个实体对象销售我的所有配置,并这样添加它们:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Approach 1: This works
    //modelBuilder.Entity<Question>().Map<DiscursiveQuestion>(p => p.Requires("TP_QUESTION").HasValue("D")).ToTable("TB_QUESTION");
    //modelBuilder.Entity<Question>().Map<ObjectiveQuestion>(p => p.Requires("TP_QUESTION").HasValue("O")).ToTable("TB_QUESTION");
    //modelBuilder.Entity<Question>().Map<VisualQuestion>(p => p.Requires("TP_QUESTION").HasValue("V")).ToTable("TB_QUESTION");
    // Approach 2: This does not work: it complains that 2 of the 3 entities cant share the TB_QUESTION table because "they are not in the same type hierarchy 
    // or do not have a valid one to one foreign key relationship with matching primary keys between them" (???).
    modelBuilder.Configurations.Add(new VisualQuestionConfiguration());
    modelBuilder.Configurations.Add(new ObjectiveQuestionConfiguration());
    modelBuilder.Configurations.Add(new DiscursiveQuestionConfiguration());
}

这些是配置对象:

public abstract class QuestionConfiguration<T> : EntityTypeConfiguration<T> where T : Question
{
    public QuestionConfiguration()
    {
        Property(p => p.Statement).HasColumnName("STATEMENT");
    }
}
public class DiscursiveQuestionConfiguration : QuestionConfiguration<DiscursiveQuestion>
{
    public DiscursiveQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("D")).ToTable("TB_QUESTION");
    }
}
public class VisualQuestionConfiguration : QuestionConfiguration<VisualQuestion>
{
    public VisualQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("V")).ToTable("TB_QUESTION");
    }
}
public class ObjectiveQuestionConfiguration : QuestionConfiguration<ObjectiveQuestion>
{
    public ObjectiveQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("O")).ToTable("TB_QUESTION");
    }
}

为什么接近1在2时工作?

编辑:

我删除了配置继承,并且几乎"工作"(见下文)。这样:

public class QuestionConfiguration : EntityTypeConfiguration<Question>
{
    public QuestionConfiguration()
    {
        Property(p => p.Statement).HasColumnName("STATEMENT");
        // Configures the TPH
        Map<VisualQuestion>(p => p.Requires("TYPE").HasValue("Visual").HasMaxLength(10));
        Map<ObjectiveQuestion>(p => p.Requires("TYPE").HasValue("Objective").HasMaxLength(10));
        Map<DiscursiveQuestion>(p => p.Requires("TYPE").HasValue("Discursive").HasMaxLength(10));
        ToTable("TB_QUESTION");
    }
}
public class DiscursiveQuestionConfiguration : Configuration<DiscursiveQuestion>
{
    public DiscursiveQuestionConfiguration()
    {
    }
}
public class VisualQuestionConfiguration : Configuration<VisualQuestion>
{
    public VisualQuestionConfiguration()
    {
    }
}
public class ObjectiveQuestionConfiguration : Configuration<ObjectiveQuestion>
{
    public ObjectiveQuestionConfiguration()
    {
    }
}
public class ObjectiveQuestionOptionConfiguration : Configuration<ObjectiveQuestionOption>
{
    public ObjectiveQuestionOptionConfiguration()
    {
        HasRequired(p => p.Question).WithMany(p => p.Options).HasForeignKey(p => p.ObjectiveQuestionId);
        Property(p => p.ObjectiveQuestionId).HasColumnName("ID_OBJECTIVE_QUESTION");
        Property(p => p.Statement).HasColumnName("STATEMENT"); // <--- This doesnt get mapped! :(
        ToTable("TB_OBJECTIVE_QUESTION_OPTION");
    }
}

并这样注册:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Approach 1: This works
    //modelBuilder.Entity<Question>().Map<DiscursiveQuestion>(p => p.Requires("TP_QUESTION").HasValue("D")).ToTable("TB_QUESTION");
    //modelBuilder.Entity<Question>().Map<ObjectiveQuestion>(p => p.Requires("TP_QUESTION").HasValue("O")).ToTable("TB_QUESTION");
    //modelBuilder.Entity<Question>().Map<VisualQuestion>(p => p.Requires("TP_QUESTION").HasValue("V")).ToTable("TB_QUESTION");
    // Approach 2: This does work too, however ObjectiveQuestionOption* does not inherit the statement column
    modelBuilder.Configurations.Add(new QuestionConfiguration());
    modelBuilder.Configurations.Add(new QuestionOptionConfiguration());
}

区别在于,第一种方法告诉EF将基本抽象类Question视为实体(modelBuilder.Entity<Question>()调用),而第二个则没有。

您需要为Question创建并注册单独的配置。由于您将配置那里的所有常见属性,因此QuestionConfiguration<T>类是多余的。

这是第二种方法的正确实现。

配置:

public class QuestionConfiguration : EntityTypeConfiguration<Question>
{
    public QuestionConfiguration()
    {
        Property(p => p.Statement).HasColumnName("STATEMENT");
        ToTable("TB_QUESTION");
    }
}
public class DiscursiveQuestionConfiguration : EntityTypeConfiguration<DiscursiveQuestion>
{
    public DiscursiveQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("D"));
    }
}
public class VisualQuestionConfiguration : EntityTypeConfiguration<VisualQuestion>
{
    public VisualQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("V"));
    }
}
public class ObjectiveQuestionConfiguration : EntityTypeConfiguration<ObjectiveQuestion>
{
    public ObjectiveQuestionConfiguration()
    {
        Map(p => p.Requires("TP_QUESTION").HasValue("O"));
    }
}

注册:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new QuestionConfiguration());
    modelBuilder.Configurations.Add(new VisualQuestionConfiguration());
    modelBuilder.Configurations.Add(new ObjectiveQuestionConfiguration());
    modelBuilder.Configurations.Add(new DiscursiveQuestionConfiguration());
}

相关内容

  • 没有找到相关文章

最新更新