《Entity Framework 6 Recipes》中文翻译系列 (33) ------ 第六章 继承与建模高级应用之TPH与TPT (2)
2015-05-31 21:47
906 查看
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
6-8 嵌套的TPH建模
问题你想使用超过一层的TPH继承映射为一张表建模。
解决方案
假设你有一张员工(Employee)表,它包含各种类型的员工,比如,钟点工,雇员。如图6-10所示。
图6-10 包含各种类型的员工表
Employee表包含钟点工,雇员,提成员工,这是雇员下面的一个子类型。按下面的步骤,使用派生类型HourlyEmployee,SalariedEmployee和SalariedEmployee的子类CommissionedEmployee为这张表建模。
1、在你的项目中创建一个继承自DbContext的上下文对象Recipe8Context;
2、创建POCO实体类 Employee、HourlyEmployee、SalariedEmployee和CommissionedEmployee,如代码清单6-23所示;
代码清单6-23.POCO实体类mployee, HourlyEmployee, SalariedEmployee和CommissionedEmployee
public abstract class Employee { public int EmployeeId { get; set; } public string Name { get; set; } } public class SalariedEmployee : Employee { public decimal? Salary { get; set; } } public class CommissionedEmployee : SalariedEmployee { public decimal? Commission { get; set; } } public class HourlyEmployee : Employee { public decimal? Rate { get; set; } public decimal? Hours { get; set; } } }
3、在上下文对象中添加一个类型为DbSet<Employee>的属性;
4、在上下文对象中重写OnModelCreating方法,配置TPH中每个派生类的鉴别值,如代码清单6-24所示;
代码清单6-24. 重写OnModelCreating方法,配置TPH中每个派生类的鉴别值
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Employee>() .HasKey(e => e.EmployeeId) .Property(e => e.EmployeeId) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); modelBuilder.Entity<Employee>() .Map<HourlyEmployee>(m => m.Requires("EmployeeType").HasValue("hourly")) .Map<SalariedEmployee>(m => m.Requires("EmployeeType").HasValue("salaried")) .Map<CommissionedEmployee>(m => m.Requires("EmployeeType").HasValue("commissioned")) .ToTable("Employee", "Chapter6"); }
原理
TPH继承映射是一种灵活的建模技术。继承树的深度和广度可以进行合理地扩展,映射也容易实现。这种方法有效率,是因为它没有引入额外的表,不涉及join连接。
使用Code-First来实现TPH简单明了,因为面向对象的继承,层次自然。
代码清单6-25演示了从模型中插入和获取。
代码清单6-25.插入并获取Employee的派生类型
using (var context = new Recipe8Context()) { var hourly = new HourlyEmployee { Name = "Will Smith", Hours = (decimal)39, Rate = 7.75M }; var salaried = new SalariedEmployee { Name = "JoAnn Woodland", Salary = 65400M }; var commissioned = new CommissionedEmployee { Name = "Joel Clark", Salary = 32500M, Commission = 20M }; context.Employees.Add(hourly); context.Employees.Add(salaried); context.Employees.Add(commissioned); context.SaveChanges(); } using (var context = new Recipe8Context()) { Console.WriteLine("All Employees"); Console.WriteLine("============="); foreach (var emp in context.Employees) { if (emp is HourlyEmployee) Console.WriteLine("{0} Hours = {1}, Rate = {2}/hour", emp.Name, ((HourlyEmployee)emp).Hours.Value.ToString(), ((HourlyEmployee)emp).Rate.Value.ToString("C")); else if (emp is CommissionedEmployee) Console.WriteLine("{0} Salary = {1}, Commission = {2}%", emp.Name, ((CommissionedEmployee)emp).Salary.Value.ToString("C"), ((CommissionedEmployee)emp).Commission.ToString()); else if (emp is SalariedEmployee) Console.WriteLine("{0} Salary = {1}", emp.Name, ((SalariedEmployee)emp).Salary.Value.ToString("C")); } }
代码清单6-25的输出如下:
All Employees ============= Will Smith Hours = 39.00, Rate = $7.75/hour JoAnn Woodland Salary = $65,400.00 Joel Clark Salary = $32,500.00, Commission = 20.00%
6-9 在TPT继承映射中应用条件
问题你想在TPT继承映射中应用条件。
解决方案
假设你有两张如图6-11所示的表。Toy(玩具)表描述一个公司的玩具产品,大部份手工制作的玩具用于销售,一部分捐献给慈善机构。在制作过程中,有些玩具可能会损坏。损坏的玩具将被翻新。一个质检员决定翻新玩具最终的质量。
图6-11 玩具(Toy)表和翻新玩具(Refurbished)表间的一对一的关系
为这家公司生成报表的应用,不需要访问用于捐献的玩具。按下面的步骤,使用TPT继承映射为Toy和RefurbishedToy表建模,同时过滤掉用于捐献的手工玩具:
1、在你的项目中添加一个ADO.NET Entity Data Model(ADO.NET实体数据模型),并导入表Toy和ReferbishedToy;
2、删除实体Toy与RefurbishedToy之间的关联;
3、右键Toy实体,选择Add(增加) ➤Inheritance(继承)。选择Toy作为基类,RefurbishedToy作为派生类;
4、从实体RefurbishedToy中删除属性ToyId;
5、选择实体RefurbishedToy,并查看Mapping Details window(映射详细信息窗口),将ToyId列映射到ToyId属性。这个值将来至基类Toy;
6、从Toy实体中删除标量属性ForDonatinOnly;
7、选择实体Toy,并查看Mapping Details window(映射详细信息窗口),使用Add a Talbe or View(添加表或视图)来映射Toy实体到实体表。添加一个条件当ForDonationOnly=0;
最终的模型如图6-12所示。
图6-12 Toy实体和它的派生类型RefurbishedToy实体的概念模型
原理
通过在基类中应用一个条件,我们限制RefurbishedToy实例为非捐献玩具。这种方法在如下的情况下非常有用,用一张单独的表来实现继承类型映射,同时使用一个固定的条件来过滤这个继承结构。
代码清单6-26 演示了从这个模型中插入和获取数据。
代码清单6-26. 从模型中插入和获取数据
using (var context = new Recipe9Context()) { context.Database.ExecuteSqlCommand(@"insert into chapter6.toy (Name,ForDonationOnly) values ('RagDoll',1)"); var toy = new Toy { Name = "Fuzzy Bear", Price = 9.97M }; var refurb = new RefurbishedToy { Name = "Derby Car", Price = 19.99M, Quality = "Ok to sell" }; context.Toys.Add(toy); context.Toys.Add(refurb); context.SaveChanges(); } using (var context = new Recipe9Context()) { Console.WriteLine("All Toys"); Console.WriteLine("========"); foreach (var toy in context.Toys) { Console.WriteLine("{0}", toy.Name); } Console.WriteLine("\nRefurbished Toys"); foreach (var toy in context.Toys.OfType<RefurbishedToy>()) { Console.WriteLine("{0}, Price = {1}, Quality = {2}", toy.Name, toy.Price, ((RefurbishedToy)toy).Quality); } }
代码清单6-26的输出如下:
All Toys ======== Fuzzy Bear Derby Car Refurbished Toys Derby Car, Price = 19.99, Quality = Ok to sell
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
相关文章推荐
- Gitlab7.0通知邮箱的配置(smtp)
- PHP设计模式——解释器模式
- 使用PHP mysqli 扩展增强库(面向对象/数据库操作封装/事务控制/预编译)总结
- laravel 学习笔记——起点
- Windows Server 2012 之文件服务器(FTP)
- Laravel 学习笔记 —— 神奇的服务容器
- Laravel 架构中的 Container/ServiceProvider/Facade
- PHP模板Smarty的具体使用指南
- PHP配置文件详解php.ini
- php利用接口实现类的多重继承
- PHP自动加载
- php 提交form给本页面
- PHP通过thrift2访问HBASE
- php学习随记2
- PHP foreach()语句的一个诡异问题
- 如何用PHPMailer基于php脚本发送邮件
- php导出word实例代码
- 织梦PHP站一直被黑客挂木马的解决办法
- Composer 在 PHP 5.6 下不能获取数据的解决方法
- php学习随记1