entity-framework - EF Code First 配置中的多级继承

标签 entity-framework ef-code-first

我有一个用于我定义的一些实体的抽象基类。这些派生实体之一实际上是另一个实体的非抽象基类。

按照此代码:

public abstract class BaseReportEntry {
      public int ReportEntryId { get; set;}
      public int ReportBundleId { get; set; }  //FK
      public virtual ReportBundle ReportBunde { get; set; } 
}

//A few different simple pocos like this one
public PerformanceReportEntry : BaseReportEntry {
     public int PerformanceAbsolute { get; set; }
     public double PerformanceRelative { get; set; }

}

//And one with a second level of inheritance 
public ByPeriodPerformanceReportEntry : PerformanceReportEntry {
       public string Period { get; set; }
}

我正在使用基本EntityTypeConfiguration:

public class BaseReportEntryMap<TReportEntry> : EntityTypeConfiguration<TReportEntry>
    where TReportEntry : BaseReportEntry
{
    public BaseReportEntryMap()
    {
        this.HasKey(e => e.ReportEntryId);

        this.HasRequired(e => e.ReportsBundle)
            .WithMany()
            .HasForeignKey(e => e.ReportsBundleId);
    }
}

大概这对于一级继承来说效果很好,但对于具有第二级继承的情况会抛出以下错误:

The foreign key component 'ReportsBundleId' is not a declared property on type 'ByPeriodPerformanceReportEntry'


public class ByPeriodPerformanceReportEntryMap : BaseReportEntryMap<ByPeriodPerformanceReportEntry>
{
    public ByPeriodPerformanceReportEntryMap ()
        : base()
    {
        this.Property(e => e.Period).IsRequired();

        this.Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("ByPeriodPerformanceReportEntries"); 
        });
    }
}

如果需要,这里是 ReportBundle 类

 public class ReportsBundle 
{
    public int ReportsBundleId { get; set; }
    public virtual ICollection<PerformanceReportEntry> PerformanceReportEntries{ get; set; }
    public virtual ICollection<ByPeriodPerformanceReportEntry> ByPeriodPerformanceReportEntries{ get; set; }
}

最佳答案

问题不在于第二级继承,而在于 PerformanceReportEntry ( ByPeriodPerformanceReportEntry 的基数)是一个实体,而 BaseReportEntry ( PerformanceReportEntry 的基数)不是。

如果PerformanceReportEntry,您的映射将起作用。不会是一个实体 - 即它的映射不会添加到模型构建器配置中,并且您没有 DbSet对于这种类型,它不会出现在 ReportsBundle 中的导航集合中.

BaseReportEntryMap<ByPeriodPerformanceReportEntry> 派生配置在这种情况下是不可能的 - 并且没有必要,因为基本属性的映射已经由 BaseReportEntryMap<PerformanceReportEntry> 发生。 。因此您可以使用

public class ByPeriodPerformanceReportEntryMap
    : EntityTypeConfiguration<ByPeriodPerformanceReportEntry>

但我怀疑生成的模型是否如您所期望的那样。我不知道PerformanceReportEntries是什么和ByPeriodPerformanceReportEntries收藏 ReportsBundle应该表达。你期望吗ByPeriodPerformanceReportEntries是按子类型过滤的集合吗?你期望吗PerformanceReportEntries仅包含 PerformanceReportEntry 的 ReportsEntries s 但不是ByPeriodPerformanceReportEntry是?你期望吗PerformanceReportEntries包含所有条目,包括 ByPeriodPerformanceReportEntries

无论如何,BaseReportEntry.ReportBundle是映射在 PerformanceReportEntry 中的导航属性(不在 ByPeriodPerformanceReportEntry 中)。这意味着类 ReportsBundle 中的反向导航属性必须引用PerformanceReportEntry这是 PerformanceReportEntries导航集合。 ByPeriodPerformanceReportEntries将在 ReportsBundle 之间引入第二个一对多关系和ByPeriodPerformanceReportEntry ( ByPeriodPerformanceReportEntry 中没有导航属性)。 ByPeriodPerformanceReportEntries的逆向导航属性不会是BaseReportEntry.ReportBundle .

我的感觉是你不应该有 ReportsBundle.ByPeriodPerformanceReportEntries集合,但我不确定你到底想要实现什么。

编辑

根据您的评论,您只有这两种报告类型,我认为您的映射太复杂了。我会执行以下操作:

  • 删除 BaseReportEntry类并将其属性移至 PerformanceReportEntry 。拥有一个只有一个其他类派生自的基类是没有意义的。

  • 删除 ByPeriodPerformanceReportEntries来自ReportsBundle ,这样ReportsBundle将是:

    public class ReportsBundle 
    {
        public int ReportsBundleId { get; set; }
        public virtual ICollection<PerformanceReportEntry>
            PerformanceReportEntries { get; set; }
    }
    
  • 删除 BaseReportEntryMap并将映射移至 PerformanceReportEntryMap 。此 map 源自 EntityTypeConfiguration<PerformanceReportEntry> .

  • 更正映射。目前这是错误的,因为您没有在WithMany中指定反向导航属性。 。 PerformanceReportEntryMap应该看起来像这样:

    public class PerformanceReportEntryMap
        : EntityTypeConfiguration<PerformanceReportEntry>
    {
        public PerformanceReportEntryMap()
        {
            this.HasKey(e => e.ReportEntryId);
    
            this.HasRequired(e => e.ReportsBundle)
                .WithMany(b => b.PerformanceReportEntries)
                .HasForeignKey(e => e.ReportsBundleId);
        }
    }
    
  • 派生ByPeriodPerformanceReportEntryMap来自EntityTypeConfiguration<ByPeriodPerformanceReportEntry>并仅指定 ByPeriodPerformanceReportEntry声明的属性的映射,不再是基本属性。这已经发生在 PerformanceReportEntryMap 。您不需要也不能再次指定它,因为它会导致您遇到的异常。

  • 使用每个层次结构表 (TPH) 继承,而不是每个具体类型表 (TPC),特别是如果您只有 ByPeriodPerformanceReportEntry 中声明的几个属性。 。 TPC 更难使用,因为它在数据库生成的身份和多态关联(在 PerformanceReportEntryReportsBundle 之间的关系中存在)方面存在问题。 The problems are explained in more details here 。相反,TPH 提供最佳性能。 ByPeriodPerformanceReportEntryMap然后看起来像这样:

    public class ByPeriodPerformanceReportEntryMap
        : EntityTypeConfiguration<ByPeriodPerformanceReportEntry>
    {
        public ByPeriodPerformanceReportEntryMap()
        {
            this.Property(e => e.Period).IsRequired();
        }
    }
    

    无需显式配置 TPH,因为它是默认继承映射。

关于entity-framework - EF Code First 配置中的多级继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15604585/

相关文章:

asp.net-mvc - 调用 TryUpdateModel 时防止 EF4 ConstraintException

c# - VS 2015 ASP.NET Web API (EF6) & Xamarin 启用迁移失败

c# - EF Code First 通用检查是否存在没有公共(public) ID 属性的对象?

c# - Entity Framework 5 Fluent API - 配置单向关系

entity-framework - 撤消 Entity Framework 中的更改,包括更改为对象引用

c# - Entity Framework 代码优先 ASP.Net 和 DbContext

c# - Asp.Net MVC 5 应用程序中所有模型的 Models.ApplicationDbContext?

entity-framework-4 - Entity Framework 4.1 Code-First 方法实现域服务上的多对多关系

entity-framework-4 - Entity Framework 4 - 在持久性未知上下文中使用 CTP5(代码优先)映射非公共(public)属性

c# - Code first DbMigrator 在从不同机器构建时导致错误