如何在EF中将两个不同的数据库表"合并"到单个继承的类层次结构中?



我有一个遗留数据库,我想写一个应用程序来监控其中的一些内容。

DB有一组表,这些表对于其中的每个"项目"都是重复的

例如,如果我有两个项目,我会有project_1_table和类似的project_2_table。

尽管这两个表为每个项目提供了相同的目的,但还是有一些细微的差异。

我正在寻找一种方法来创建一个模型,该模型将有一个包含这些表的所有公共元素的基类,然后有两个继承的类来映射实际内容。

澄清:我无法控制DB,也无法更改它。正在寻找在这种情况下使用EF的最佳方式

如何做到这一点(在模型编辑器中或通过CodeFirst)?

DB架构

project_1_table                   project_2_table
recordID                          recordID
title                             title
project1field                     project2field

实体模型(我想要的)

BaseEntity
ProjectType
RecordID
Title
(Mapped inherited Entities)
Project1Entity: BaseEntity        Project2Entity: BaseEntity
ProjectType = 1                   ProjectType = 2
RecordID                          RecordID
Title                             Title
Project1Field                     Project2Field

我是EF的新手,它似乎让我难以捉摸。

这可能吗?如果是,如何?

通过使用Table-per-ConcreteType继承模型,可以实现与您的需求类似的东西。

实体

public abstract class BaseEntity {
public int RecordID { get; set; }
public abstract int ProjectType { get; }
public string Title { get; set; }
}
public class Project1Entity : BaseEntity {
public override int ProjectType {
get { return 1; }
}
public string Project1Field { get; set; }
}
public class Project2Entity : BaseEntity {
public override int ProjectType {
get { return 2; }
}
public string Project2Field { get; set; }
}

DBContext

public class DataContext : DbContext {
public DbSet<Project1Entity> Project1Entities { get; set; }
public DbSet<Project2Entity> Project2Entities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BaseEntity>().HasKey(o => o.RecordID);
modelBuilder.Entity<Project1Entity>().Map(m => {
m.MapInheritedProperties();
m.ToTable("project_1_table");
});
modelBuilder.Entity<Project2Entity>().Map(m => {
m.MapInheritedProperties();
m.ToTable("project_2_table");
});
}
}

但是有几个限制

  • 使用此配置,EF无法为新记录生成RecordID,因此您必须自己提供主键的值
  • 它只有在project_1_table和project_2_table中的项的CCD_ 2不冲突时才起作用。如果是这样,则需要指定另一个主键,例如,将列ProjectType添加到数据库并使用复合主键{RecordID, ProjectType}

带有复合密钥的版本

实体

public abstract class BaseEntity {
public int RecordID { get; set; }
public int ProjectType { get; set; }
public string Title { get; set; }
}
public class Project1Entity : BaseEntity {
public string Project1Field { get; set; }
}
public class Project2Entity : BaseEntity {
public string Project2Field { get; set; }
}

DBContext

public class DataContext : DbContext {
...
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
...
modelBuilder.Entity<BaseEntity>().HasKey(o => new { o.RecordID, o.ProjectType });
...
}
}

如果你这样建模:

public abstract class BaseEntity
{
[Key]
public RecordID { get; set; }

public string Title { get; set; }
}
public class Project1Entity: BaseEntity
{
public string Project1Field { get; set; }
}
public class Project2Entity: BaseEntity
{
public string Project2Field { get; set; }
}

然后实体框架将创建一个单独的表,如下所示:

BaseEntity
RecordID
Title
Project1Field                     
Project2Field
Discriminator

";鉴别器";列意味着实体框架知道从数据库检索时要实例化什么类型的对象,并且应该使ProjectType字段成为冗余字段。(如果你有BaseEntity,那么你可以使用if(entity is Project1Entity)测试C#中的类型)

这种策略被称为"每层次表继承"。它是EF中默认的继承策略,如果您的继承不太复杂,它可能更适合您的目的。

如果你不喜欢单一的表,而喜欢标准化的形式,那么你可以通过向子类添加表属性来改变这一点:

[Table("Project1")]
public class Project1Entity: BaseEntity
{
public string Project1Field { get; set; }
}
[Table("Project2")]
public class Project2Entity: BaseEntity
{
public string Project2Field { get; set; }
}

这将产生这样的表格:

BaseEntity
RecordID
Title
Project1
Project1Field       
Project2
Project2Field

所以现在它被正常化了,但每次都需要一个内部连接。这种策略被称为每种类型的表

第三种选择是每种具体类型的表格。如果你真的觉得最适合你,这里有一篇完整的文章,并且有选择策略的指导方针。以下是一些我认为值得一读的名言:

默认情况下,仅针对简单问题选择TPH。对于更复杂的案例(或者当您被坚持可空性约束和规范化的重要性)考虑TPT策略。但在这一点上,问问自己将继承重塑为对象中的委派可能不会更好模型(委托是使组合功能强大以便于重用的一种方式作为继承)。对于所有人来说,通常最好避免复杂的继承与持久性或ORM 无关的各种原因

我建议TPC(仅)用于类层次结构的顶层,在通常不需要多态性的情况下未来的基本类不太可能是

最新更新