原文:Implementing Inheritance with the Entity Framework 6 in an ASP.NET MVC 5 Application

1.选择继承映射到数据库表

  在School数据模型里面,Instructor和Student类有几个属性是相同的:

  假设我们想要消除Instructor和Student实体属性的冗余代码。或者我们想要编写一个可以格式化name的服务,而不用考虑这个name是来自一个instructor还是student。我们可以创建一个只包含共有属性的Person基类,然后让Instructor和Student实体继承该基类,如下图:

  有几种方法可以把这种继承结构表示在数据库中。我们可以新建一个Person表同时包含student和instructor信息。一些列(HireDate)只用于instructor,一些列(EnrollmentDate)只用于student,一些列(LastName,FirstName)两者均可用。通常,我们需要一个鉴别(discriminator)列来表明每列代表的类型。例如,对于instructor其鉴别列的值可以为“Instructor”,student其鉴别列的值可以为“Student”:

  这种从一张数据表产生实体继承结构的模式被称作table-per-hierarchy(TPH)继承。

  另一种方法使数据库看起来更像继承结构。例如,在Person表中只有name列,在单独的Instructor和Student表中有date列:

  这种为每个实体类产生数据表的模式称为table per type(TPT)继承。

  还有一个选择是所有非抽象类型映射到单独的表。类的所有属性,包括继承属性映射到相应的表。这种模式称为Table-per-Concrete(TPC)继承。如果对Person,Student和Instructor我们采用TPC继承,Student和Instructor表在实现继承后与之前的表结构没有任何不同。

  在EF中,相对于TPT,TPC和TPH继承模式通常会有比较好的性能,所以我们要做的就是创建一个Person类,然后改变Instructor和Student类继承Person,胎哪家新类到DbContext,然后创建迁移(更多关于如何实现其他继承方式,请参考:Mapping the Table-Per-Type (TPT) InheritanceMapping the Table-Per-Concrete Class (TPC) Inheritance)。

2.创建Person类

  在Models文件夹添加Person.cs

    public abstract class Person
{
public int ID { get; set; } [Required]
[StringLength()]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
[Display(Name = "First Name")]
public string FirstMidName { get; set; } [Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}

3.Student和Instructor类继承Person

    public class Instructor : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; } public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
    public class Student : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}

4.添加Person实体到模型

  在SchoolContext.cs添加:

public DbSet<Person> People { get; set; }

  这就为EF实现table-per-hierarchy继承添加了所有配置。当数据库更新后,Person表将会取代Student和Instructor表。

5.创建和更新迁移文件

  在Package Manager Console中输入命令:

Add-Migration Inheritance

  此时运行Update-Database命令会报错,因为数据库中有数据,而迁移不知道如何处理他们,错误如下:

Could not drop object 'dbo.Instructor' because it is referenced by a FOREIGN KEY constraint.

  打开Migrations\<timestamp>_Inheritance.cs,修改过Up方法:

public override void Up()
{
// Drop foreign keys and indexes that point to tables we're going to drop.
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropIndex("dbo.Enrollment", new[] { "StudentID" }); RenameTable(name: "dbo.Instructor", newName: "Person");
AddColumn("dbo.Person", "EnrollmentDate", c => c.DateTime());
AddColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: , defaultValue: "Instructor"));
AlterColumn("dbo.Person", "HireDate", c => c.DateTime());
AddColumn("dbo.Person", "OldId", c => c.Int(nullable: true)); // Copy existing Student data into new Person table.
Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, ID AS OldId FROM dbo.Student"); // Fix up existing relationships to match new PK's.
Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')"); // Remove temporary key
DropColumn("dbo.Person", "OldId"); DropTable("dbo.Student"); // Re-create foreign keys and indexes pointing to new table.
AddForeignKey("dbo.Enrollment", "StudentID", "dbo.Person", "ID", cascadeDelete: true);
CreateIndex("dbo.Enrollment", "StudentID");
}

  (如果我们使用的是GUID而不是integer作为主键的类型,student的主键值可以不必更改,一些步骤可以省略)

  运行update-database命令。

  (如果在产品上,为了确保万一需要修改回之前的数据库版本,我们在Down方法里也要做相应的修改)

6.测试

  新数据库的结构:

7.部署到Azure

更多关于继承,请查看:TPT Inheritance PatternTPH Inheritance Pattern

[翻译][MVC 5 + EF 6] 11:实现继承的更多相关文章

  1. 7.翻译系列:EF 6中的继承策略(EF 6 Code-First 系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx EF 6 ...

  2. [翻译][MVC 5 + EF 6] 1:创建数据模型

    原文:Getting Started with Entity Framework 6 Code First using MVC 5 1.新建MVC项目: 2.修改Views\Shared\_Layou ...

  3. [翻译][MVC 5 + EF 6] 7:加载相关数据

    原文:Reading Related Data with the Entity Framework in an ASP.NET MVC Application 1.延迟(Lazy)加载.预先(Eage ...

  4. [翻译][MVC 5 + EF 6] 6:创建更复杂的数据模型

    原文:Creating a More Complex Data Model for an ASP.NET MVC Application 前面的教程中,我们使用的是由三个实体组成的简单的数据模型.在本 ...

  5. [翻译][MVC 5 + EF 6] 5:Code First数据库迁移与程序部署

    原文:Code First Migrations and Deployment with the Entity Framework in an ASP.NET MVC Application 1.启用 ...

  6. [翻译][MVC 5 + EF 6] 4:弹性连接和命令拦截

    原文:Connection Resiliency and Command Interception with the Entity Framework in an ASP.NET MVC Applic ...

  7. [翻译][MVC 5 + EF 6] 12[完结]:高级场景

    原文:Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application 1.执行原生SQL查询: EF Code First API ...

  8. [翻译][MVC 5 + EF 6] 10:处理并发

    原文:Handling Concurrency with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.并发冲突: 当一个用户编辑一 ...

  9. [翻译][MVC 5 + EF 6] 9:异步和存储过程

    原文:Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application 1.为什么使用异步代码: ...

随机推荐

  1. android studio 真机调试

    1.使用usb连接电脑,打开开发者选项,打开usb调试. 2.不能使用仅充电,修改为媒体设备(MTP) 3.如果无法连接可以使用豌豆荚或者金山手机助手连接. 4.运行android程序即可. 5.如果 ...

  2. MySQL通过RPM安装

    以前写过一篇文章,RedHat Linux 6.1 安装MySQL,本文是从解决依赖的角度上再次描述如何在Linux下以RPM包方式安装MySQL. [root@serv01 ~]# ls /iso/ ...

  3. 利用sqlmap和burpsuite绕过csrf token进行SQL注入 (转)

    问题:post方式的注入验证时遇到了csrf token的阻止,原因是csrf是一次性的,失效导致无法测试. 解决方案:Sqlmap配合burpsuite,以下为详细过程,参照国外牛人的blog(不过 ...

  4. STL 源代码剖析 算法 stl_algo.h -- lower_bound

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie lower_bound(应用于有序区间) ------------------------- ...

  5. Xcode repository host is unreachable

    遇到这个错误,首先不要急.按照如下方法即可(如果你的svn地址没有问题的话): url要使用域名,所以映射下 1. 修改host:在应用程序里面打开终端(terminal),输入 sudo vi /e ...

  6. Nginx详细配置

    #运行用户#user  nobody; #启动进程,通常设置成和cpu的数量相等或者2倍于cpu的个数(具体结合cpu和内存).默认为1worker_processes  1; #全局的错误日志和日志 ...

  7. iOS开发——UI篇OC篇&SpriteKit详解

    SpriteKit详解 SpriteKit,iOS/Mac游戏制作的新纪元 这是我的WWDC2013系列笔记中的一篇,完整的笔记列表请参看这篇总览.本文仅作为个人记录使用,也欢迎在许可协议范围内转载或 ...

  8. 内核日志及printk结构浅析

      作者:tekkamanninja 鸣谢:感谢ChinaUnix技术社区的tekkamanninja提供稿件 ,如需转载,请注明出处. 这段时间复习了一下内核调试系统,注意看了一下printk的实现 ...

  9. 【转】使用BBB的device tree和cape(重新整理版)

    只要你想用BBB做哪怕一丁点涉及到硬件的东西,你就不可避免地要用到cape和device tree的知识.所以尽管它们看起来很陌生而且有点复杂,但还是得学.其实用起来不难的.下面我只讲使用时必须会的内 ...

  10. Hibernate学习笔记--------3.缓存

    一.一级缓存 一级缓存又称为“Session缓存”或者“会话级缓存”,通过Session从数据库查询实体时,会把实体在内存中存储起来,下一次查询同一实体时不再再次执行sql语句查询数据库,而是从内存中 ...