我有一个遗留数据库,我想写一个应用程序来监控其中的一些内容。
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(仅)用于类层次结构的顶层,在通常不需要多态性的情况下未来的基本类不太可能是