【问题标题】:Map relationship to base class in Entity Framework将关系映射到实体框架中的基类
【发布时间】:2018-09-12 16:02:01
【问题描述】:

如果我有类似的课程

public class BaseEntity
{
    public int Id {get;set;}
    public DateTime CreatedOn {get;set;}
    public DateTime ModifiedOn {get;set;}

    public IList<ModuleHistory> History {get;set;}
}

public class FirstEntity : BaseEntity
{
    public string FirstEntityProperty {get;set;}
}

public class SecondEntity : BaseEntity
{
    public string SecondEntityProperty {get;set;}
}

public class ModuleHistory
{
    public string Field {get;set;}
    public string FromValue {get;set;}
    public string ToValue {get;set;}

    public BaseEntity Module {get;set;}
}

我想要一张表用于 FirstEntities,一张表用于 SecondEntities,一张表用于 ModuleHistories。我想,ModuleHistory 需要为 BaseEntityId 提供一个列,为 BaseEntityType 提供一个列(FirstEntity,SecondEntity)。我第一次尝试这个工作,但导致创建一个 BaseEntities 表。 EF 是否可以在没有基本实体表的情况下进行映射?

【问题讨论】:

    标签: c# entity-framework inheritance


    【解决方案1】:

    要使用每个具体类型的表,请使用MapInheritedProperties 告诉 EF 基类属性是子类表的一部分。

    对于 EntityTypeConfiguration 设置:

    public class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
    {
      public MyEntityConfiguration()
      {
        Map(x => x.MapInheritedProperties());
        ToTable("MyEntities");
      }
    }
    

    如果您使用模型构建器来设置实体,那么:

    modelBuilder.Entity<MyEntity>().Map(m =>
    {
      m.MapInheritedProperties();
      m.ToTable("MyEntities");
    }
    

    在下方编辑 (也编辑了上面的示例,MapInheritiedProperties 必须在 ToTable 之前)

    如果您想要基类级别的子关系,那么您应该将数据模型构建为按层次结构的表,而不是按具体的表,因为表之间的 FK 关系是一对一的。 ModuleHistory 不能有指向“FirstEntity”或“SecondEntity”的 FK。

    可以让 EF 对此进行映射,但我不确定是否要使用 Code-First 进行尝试。使用实体配置:

    给定原始示例中的类结构:

    public class FirstEntityConfiguration : EntityTypeConfiguration<FirstEntity>
    {
        public FirstEntityConfiguration()
        {
            Map(x => x.MapInheritedProperties());
            ToTable("FirstEntity");
            HasKey(x => x.Id);
    
            HasMany(x => x.History)
                .WithOptional(x => (FirstEntity)x.Module)
                .Map(x => x.MapKey("ModuleId"));
        }
    }
    
    public class SecondEntityConfiguration : SecondEntityConfiguration<SecondEntity>
    {
        public SecondEntityConfiguration()
        {
            Map(x => x.MapInheritedProperties());
            ToTable("SecondEntity");
            HasKey(x => x.Id);
    
            HasMany(x => x.History)
                .WithOptional(x => (SecondEntity)x.Parent)
                .Map(x => x.MapKey("ModuleId"));
        }
    }
    
    public class ModuleHistoryConfiguration : EntityTypeConfiguration<ModuleHistory>
    {
        public ModuleHistoryConfiguration()
        {
            ToTable("ModuleHistory");
            HasKey(x => x.Id);
        }
    }
    
    // This mapping is needed because ModuleHistory's reference to BaseEntity. EF needs to know there is a Key.
        public class BaseEntityConfiguration : EntityTypeConfiguration<BaseEntity>
        {
            public BaseEntityConfiguration()
            {
                HasKey(x => x.Id);
            }
        }
    

    然后,当您获取 FirstEntity 或 SecondEntity 时,您将收到任何历史记录。

    这个实现有几个严重的警告:

    1. 您失去了对 Module History 的 FK 约束,因此数据库不会为孤立(无级联删除)或历史与父级之间的正确关联提供任何保护。
    2. 您必须对 TpCC 表上的 PK 负责,以避免在 FirstEntity 和 SecondEntity 之间重复密钥的可能性。此处的选项包括表之间的共享序列,或使用 UniqueIdentifier (/w NewSequentialId())

    最终,如果您希望基类中的子引用,您可能会更好地使用按层次结构表配置,否则将历史记录分为两个类并将关系移至实现类。

    【讨论】:

    • 这仍然会导致创建 BaseEntities 表。我还尝试将 IList 移动到 2 个实体,但这会导致 ModuleHistories 表获取 FirstEntity_Id 和 SecondEntity_Id 列。我想要一个——Entity_Id,加上一个鉴别器。
    • Table per Concrete 不能很好地为您提供基类中的子引用。问题不在于 EF,因为您可以破解它来工作,问题在于 SQL Server。 FK 关系仅针对 1 个表。编辑以显示如何使用 EF 与 DB First 减去 FK 约束在子级和父级之间完成。
    猜你喜欢
    • 2019-03-27
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-10
    相关资源
    最近更新 更多