Contoso 大学 - 8 – 实现继承
By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's Web Platform & Tools Content Team.
原文地址:http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application
全文目录:Contoso 大学 - 使用 EF Code First 创建 MVC 应用
在上一次教程中,你已经能够处理并发异常。这个教程将会展示如何在数据模型中实现继承。
在面向对象的程序设计中,你可以通过继承来清除冗余的代码。在这个教程中,你将要通过修改教师 Instructor 和学生 Student 类,以便使他们从包含类似 LastName 属性的 Person 类中派生。对于 Web 页面不需要任何改动,你需要修改一点代码,这些修改将会被自动反射到数据库中。
8-1 单表继承 (Table-per-Hierarchy) 对类型表 (Table-per-Type) 继承
在面向对象的程序设计中,你可以通过对相关的类使用继承来使得工作更加简单。例如,教师 Instructor 和学生 Student 类在学校 School 数据模型中共享多个属性,带来了冗余的代码。
假设你希望清除在教师 Instructor 和学生 Student 之间所共享的属性带来的冗余代码。可以创建一个 Person 基类,其中仅仅包含他们共享的属性,然后,使得教师 Instructor 和学生 Student 类从 Person 基类派生,如下图所示。
在数据库中这种继承结构可以有多种表现形式,可以创建一个名为 Person 的表,在这个独立的表中包含教师和学生所有的信息。既包括他们独自拥有的属性 ( 例如教师的 HireDate ,以及学生的 EnrollmentDate ),也包括它们共有的属性 ( 例如 LastName, FirstName )。通常你还需要一个用于识别当前类型的列 discriminator 来标识当前行的类型。( 在这里,标识列的内容为 Instructor 来表示教师,Student 来表示学生 )
使用单个数据库表来生成实体继承结构的模式称为单表继承模式 TPH (table-per-hierarchy )。
另外一种方式是使得数据库看起来类似继承结构。例如,在 Person 表中仅仅包含他们共有的名字属性,而将不同的时间分别保存到独立的 Instructor 和 Student 表中。
这种每种实体类对应一张数据库表的模式称为类型表 TPT 继承 (table per type )。
在 EF 中,TPH 继承比 TPT 继承有更好的性能,因为 TPT 继承需要复杂的连接查询。这个教程演示如何实现 TPH 继承。你需要完成如下的步骤:
- 创建 Person 类,将 Instructor 和 Student 类从 Person 类中派生
- 在数据库上下文类中增加模型到数据库的映射代码
- 将项目中的 InstructorID 和 StudentID 修改为使用 PersonID.
8-2 创建 Person 类
在 Model 文件夹中,创建 Person.cs ,使用下面的代码替换原有的代码。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models
{
public abstract class Person
{
[Key]
public int PersonID { get; set; } [Required(ErrorMessage = "Last name is required.")]
[Display(Name="Last Name")]
[MaxLength(50)]
public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")]
[Column("FirstName")]
[Display(Name = "First Name")]
[MaxLength(50)]
public string FirstMidName { get; set; } public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
}
在 Instructor.cs 文件中,将 Instructor 从 Person 派生出来,删除 key 和 name 字段。代码如下所示。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models
{
public class Instructor : Person
{
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Required(ErrorMessage = "Hire date is required.")]
[Display(Name = "Hire Date")]
public DateTime? HireDate { get; set; } public virtual ICollection<Course> Courses { get; set; } public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
对 Student.cs 文件进行类似的修改,修改后的 Student 类如下所示。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models
{
public class Student : Person
{
[Required(ErrorMessage = "Enrollment date is required.")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime? EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
8-3 在模型中增加 Person 实体类型
在 SchoolContext.cs 文件中,对 Person 实体类型增加一个 DbSet 类型的属性。
public DbSet<Person> People { get; set; }
这就是在 EF 中配置 TPH 继承所有需要完成的工作。如你所见,当数据库重建的时候,EF 会自动创建 Person 表。
8-4 修改 InstructorID 和 StudentID 为 PersonID
在 SchoolContext.cs 文件中,在教师对课程的映射语句中,将 MapRightKey(“InstructorID”) 修改为 MapRightKey(“PersonID”)。
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("PersonID")
.ToTable("CourseInstructor"));
这个修改不是必须的,它仅仅修改了在表多对多连接中 InstructorID 的名字。如果你保留名为仍然为 InstructorID,程序还是可以正常工作的。
下一步,进行一次全局的替换 ( 项目中所有的文件 ),将 InstructorID 修改为 PersonID,StudentID 修改为 PersonID。注意区分大小写。( 这里演示了使用类名加上 ID 作为主键的一个缺点。如果你没有使用类名加上 ID 作为主键,这里就不需要重新命名 )
8-5 在初始化器中修改主键的值
在 SchoolInitializer.cs 中,代码假设对于 Student 和 Instructor 实体的主键数字是分离的。这对于 Student 实体来说仍然正确 ( 他们仍然从 1 到 8 ),但是对于 Instructor 实体来说,将不再从 1-5 而是修改为 9-13,因为在初始化类中的代码将教师增加在同一张表的学生之后。使用下面的代码修改 Department 和 OfficeAssignment 实体中的代码,以便使用新的教师 ID。
var departments = new List<Department>
{
new Department { Name = "English", Budget = 350000, StartDate = DateTime.Parse("2007-09-01"), PersonID = 9 },
new Department { Name = "Mathematics", Budget = 100000, StartDate = DateTime.Parse("2007-09-01"), PersonID = 10 },
new Department { Name = "Engineering", Budget = 350000, StartDate = DateTime.Parse("2007-09-01"), PersonID = 11 },
new Department { Name = "Economics", Budget = 100000, StartDate = DateTime.Parse("2007-09-01"), PersonID = 12 }
};
var officeAssignments = new List<OfficeAssignment>
{
new OfficeAssignment { PersonID = 9, Location = "Smith 17" },
new OfficeAssignment { PersonID = 10, Location = "Gowan 27" },
new OfficeAssignment { PersonID = 11, Location = "Thompson 304" },
};
8-6 将 OfficeAssignment 调整为延迟加载
当前版本的 EF 对导航属性使用了 TPH继承模式后的派生类,在一对一,或者一对零的关系上不支持预先加载模式。这对于 Instructor 实体上的 OfficeAssignment 属性是个问题。解决这个问题,需要删除在这个属性上使用的预先加载处理。
在 InstructorController.cs 文件中,删除三次出现的如下代码。
.Include(i => i.OfficeAssignment)
8-7 测试
运行程序,在各个页面上检查一下,所有的工作如以前一样。
在解决方案管理器上,双击 School.sdf 数据库,在服务器资源管理器中打开,展开 School.sdf,然后选择 Tables,你会看到 Student 和 Instructor 表已经被 Person 表替换掉了。展开 Person 表,你会看到其中拥有原来 Student 和 Instructor 中所有的列,加上 discriminator 列。
下图展示了新的 School 数据库的结构。
对于 Person ,Student 和 Instructor 类,通过 TPH 实现了继承。对于更多的关于其他继承结构,可以查看 Morteza Manavi 的博客 Inheritance Mapping Strategies。在下一次的教程中,将会学习实现仓储和单元模式的一些途径。
Contoso 大学 - 8 – 实现继承的更多相关文章
- Contoso 大学 - 9 - 实现仓储和工作单元模式
原文 Contoso 大学 - 9 - 实现仓储和工作单元模式 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Micros ...
- Contoso 大学 - 7 – 处理并发
原文 Contoso 大学 - 7 – 处理并发 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's W ...
- Contoso 大学 - 4 - 创建更加复杂的数据模型
原文 Contoso 大学 - 4 - 创建更加复杂的数据模型 原文地址:http://www.asp.net/mvc/tutorials/getting-started-with-ef-using- ...
- Contoso 大学 - 使用 EF Code First 创建 MVC 应用
原文 Contoso 大学 - 使用 EF Code First 创建 MVC 应用 Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Co ...
- Contoso 大学 - 使用 EF Code First 创建 MVC 应用,实例演练
Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Contoso 大学是虚构的.应用包括了类似学生注册.课程创建以及教师分配等功能. 这个系 ...
- Contoso 大学 - 10 - 高级 EF 应用场景
原文 Contoso 大学 - 10 - 高级 EF 应用场景 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Micros ...
- Contoso 大学 - 6 – 更新关联数据
原文 Contoso 大学 - 6 – 更新关联数据 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's ...
- Contoso 大学 - 5 – 读取关联数据
原文 Contoso 大学 - 5 – 读取关联数据 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's ...
- Contoso 大学 - 3 - 排序、过滤及分页
原文 Contoso 大学 - 3 - 排序.过滤及分页 目录 Contoso 大学 - 使用 EF Code First 创建 MVC 应用 原文地址:http://www.asp.net/mvc/ ...
随机推荐
- C#关于静态与非静态的区别
C#静态方法与非静态方法的区别不仅仅是概念上的,那么他们有什么具体的区别呢?让我们通过本文向你做一下解析. C#的类中可以包含两种方法:C#静态方法与非静态方法.那么他们的定义有什么不同呢?他们在使用 ...
- Hadoop平台配置总结
hadoop的配置,个人感觉是非常容易出问题.一个原因是要配置的地方多,还有个原因就是集群配置要在几台机器上都配置正确,才能保证配置好hadoop,跑起任务. 经过昨晚加今天上午的折腾,总算成功配好了 ...
- 深入浅出之Smarty模板引擎工作机制(二)
源代码下载地址:深入浅出之Smarty模板引擎工作机制 接下来根据以下的Smarty模板引擎原理流程图开发一个自己的模板引擎用于学习,以便加深理解. Smarty模板引擎的原理,其实是这么一个过程: ...
- MEF 编程指南(十):重组
有些应用程序被设计成在运行时动态地改变.例如,一个新的扩展可能被下载,或者其他原因变得不可用.MEF 依靠我们称之为重组(Composition)的技术处理,在初始化组合以后改变导入值的场景. 导 ...
- maven for eclipse在线安装
在线安装 地址变了下面的: http://download.eclipse.org/technology/m2e/releases Eclipse Indigo安装Maven插件Maven ...
- PL/pgSQL的 RETURN NEXT例子
从网上找到例子: 可以说,RETURN NEXT要用在循环中: 例子一: 数据准备: CREATE TABLE foo (fooid INT, foosubid INT, fooname TEXT); ...
- leetcode第一刷_Count and Say
水题. 描写叙述的还挺麻烦的,实际上就是纸老虎,用两个string,一个存上一轮的结果,一个用来更新出这一轮的结果,每次扫描上一轮,统计一个字符出现的次数,然后把这个次数和字符增加到这一轮的字符串中就 ...
- 【WinForm】C# 采用POST登录京东
C# POST 传值登录 京东 想做一个DEMO 练练html的传值和接收,就用Winform 做了一个登录京东的程序. 首先参考的网址是: 艹蛋的青春じ 让我蛋疼ミ:http://www.cnblo ...
- HDU 4586 A - Play the Dice 找规律
A - Play the DiceTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/ ...
- C# 启动外部程序的几种方法
. 启动外部程序,不等待其退出. . 启动外部程序,等待其退出. . 启动外部程序,无限等待其退出. . 启动外部程序,通过事件监视其退出. // using System.Diagnostics; ...