Entity Framework 4.1 之七:继承
来源:互联网 发布:杰茜莱旗下品牌js官网 编辑:程序博客网 时间:2024/05/22 14:17
在 ORM 文献中,有三种方式将对象的继承关系映射到表中。
- 每个类型一张表 TPT: 在继承层次中的每个类都分别映射到数据库中的一张表,彼此之间通过外键关联。
- 继承层次中所有的类型一张表 TPH:对于继承层次中的所有类型都映射到一张表中,所有的数据都在这张表中。
- 每种实现类型一张表 TPC: 有点像其他两个的混合,对于每种实现类型映射到一张表,抽象类型像 TPH 一样展开到表中。
这里我将讨论 TPT 和 TPH,EF 的好处是可以混合使用这些方式。
TPT 方式
让我们从每种类型一张表开始,我定义了一个简单的继承层次,一个抽象基类和两个派生类。
{
publicint PersonID {get; set; }
[Required]
publicstring FirstName {get; set; }
[Required]
publicstring LastName {get; set; }
publicint Age {get; set; }
}
publicclass Worker : PersonBase
{
publicdecimal AnnualSalary {get; set; }
}
publicclass Retired : PersonBase
{
publicdecimal MonthlyPension {get; set; }
}
你需要告诉模型构建器如何映射到表中。
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PersonBase>().HasKey(x=> x.PersonID);
modelBuilder.Entity<PersonBase>().Property(x=> x.PersonID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// TPT mapping
modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
modelBuilder.Entity<Worker>().ToTable("tpt.Worker");
modelBuilder.Entity<Retired>().ToTable("tpt.Retired");
}
我们使用默认的命名映射约定,模型构建器使用这些信息用 TPT 来创建数据库。
我们使用模型来跑一些代码,让我们理解如何使用上面的映射,基本上,我们仅仅使用一个 DbSet,一个 PersonBase 的集合,EF 会管理每一个成员的实际类型。
{
using (var context1=new TptContext())
{
var worker =new Worker
{
AnnualSalary =20000,
Age =25,
FirstName ="Joe",
LastName ="Plumber"
};
var retired =new Retired
{
MonthlyPension =1500,
Age =22,
FirstName ="Mike",
LastName ="Smith"
};
// Make sure the tables are empty…
foreach (var entityin context1.Persons)
{
context1.Persons.Remove(entity);
}
context1.Persons.Add(worker);
context1.Persons.Add(retired);
context1.SaveChanges();
}
using (var context2=new TptContext())
{
Console.WriteLine("Persons count:"+ context2.Persons.OfType<PersonBase>().Count());
Console.WriteLine("Worker:"+ context2.Persons.OfType<Worker>().Count());
Console.WriteLine("Retired:"+ context2.Persons.OfType<Retired>().Count());
}
}
这真的很强大,我们可以通过访问 Workers 来仅仅访问 Workers 表。
TPH 方式
TPH 是 EF 实际上默认支持的。我们可以简单地注释到前面例子中的对表的映射来使用默认的机制。
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PersonBase>().HasKey(x=> x.PersonID);
modelBuilder.Entity<PersonBase>().Property(x=> x.PersonID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// TPT mapping
//modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
//modelBuilder.Entity<Worker>().ToTable("tpt.Worker");
//modelBuilder.Entity<Retired>().ToTable("tpt.Retired");
}
结果是现在使用一张表来影射整个的继承层次。
注意到整个的层次被展开到一张表中。基类中没有的属性被自动标记为可空。还有一个额外的区分列,如果运行前面的例子,我们将会看到这个区分列的内容。
当 EF 读取一行的时候,区分列被 EF 用来知道应该创建实例的类型,因为现在所有的类都被映射到了一张表中。
也可以覆盖这一点,下面我们看一下同时混合使用 TPH 和 TPT。我定义了 Worker 的两个子类,我希望将这两个类和 Worker 基类映射到一张表。
{
publicint? ManagedEmployeesCount {get; set; }
}
publicclass FreeLancer : Worker
{
[Required]
publicstring IncCompanyName {get; set; }
}
注意到每一个属性都必须是可空的。这在 TPH 中非常不方便:每一个属性都必须是可空的。现在我们使用模型构建器来完成。
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PersonBase>().HasKey(x=> x.PersonID);
modelBuilder.Entity<PersonBase>().Property(x=> x.PersonID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// TPT mapping
modelBuilder.Entity<PersonBase>().ToTable("tpt.Person");
modelBuilder.Entity<Retired>().ToTable("tpt.Retired");
// TPH mapping
modelBuilder.Entity<Worker>()
.Map<FreeLancer>(m=> m.Requires(f=> f.IncCompanyName).HasValue())
.Map<Manager>(m=> m.Requires(ma=> ma.ManagedEmployeesCount).HasValue())
.ToTable("tph.Worker");
}
这里我使用了一种区分的方法:与默认不同,我要求属于类的列是非空的列。
使用者不需要与 TPT 区分,甚至在修改了映射之后不会影响使用的代码。
{
using (var context1=new HierarchyContext())
{
var worker =new Worker
{
AnnualSalary =20000,
Age =25,
FirstName ="Joe",
LastName ="Plumber"
};
var freeLancer =new FreeLancer
{
Age =22,
FirstName ="Mike",
LastName ="Smith",
IncCompanyName ="Mike & Mike Inc"
};
var manager =new Manager
{
Age =43,
FirstName ="George",
LastName ="Costanza",
ManagedEmployeesCount =12
};
// Make sure the tables are empty…
foreach (var entityin context1.Persons)
{
context1.Persons.Remove(entity);
}
context1.Persons.Add(worker);
context1.Persons.Add(freeLancer);
context1.Persons.Add(manager);
context1.SaveChanges();
}
using (var context2=new HierarchyContext())
{
Console.WriteLine("Persons count:"+ context2.Persons.OfType<PersonBase>().Count());
Console.WriteLine("Worker:"+ context2.Persons.OfType<Worker>().Count());
Console.WriteLine("Retired:"+ context2.Persons.OfType<Retired>().Count());
Console.WriteLine("FreeLancer:"+ context2.Persons.OfType<FreeLancer>().Count());
Console.WriteLine("Manager:"+ context2.Persons.OfType<Manager>().Count());
}
}
SQL 中的架构如下,这里混合使用了 TPT 和 TPH 。
- Entity Framework 4.1 之七:继承
- MVC5 Entity Framework学习之实现继承
- Entity Framework 4.1/4.3 之七 (DBContext 之4 数据验证)
- Entity Framework 学习总结之七:EntitySQL 基本查询
- Entity Framework 4.1 之六:乐观并发
- Entity Framework 4.1 之四:复杂类型
- Entity Framework 4.1 之六:乐观并发
- Entity Framework之犹豫不决
- Entity Framework 4.1
- Entity Framework之问题收集
- Entity Framework之问题收集
- Entity Framework之查询总结
- Entity Framework之深入分析
- Entity Framework之Code First
- Entity Framework之深入分析
- MVC5 Entity Framework学习之创建Entity Framework数据模型
- MVC5 Entity Framework学习之Entity Framework高级功能
- 转载]【翻译】Entity Framework 4.1 之六:乐观并发
- Entity Framework 4.1 之三 : 贪婪加载和延迟加载
- 最近碰到几个c++问题
- Entity Framework 4.1 之四:复杂类型
- Entity Framework 4.1 之五:多对多的关系
- Entity Framework 4.1 之六:乐观并发
- Entity Framework 4.1 之七:继承
- Entity Framework 4.1 之八:绕过 EF 查询映射
- Entity Framework 4.1 Code First 学习之路
- Entity Framework 4.1 Code-First 学习笔记
- Fluent NHibernate之旅一
- 永远的错误,不理解的结果
- Fluent NHibernate之旅二--Entity Mapping
- Unknown error: Unable to build: the file dx.jar was not loaded from the SDK folder!
- Fluent NHibernate之旅(三)-- 继承