原文: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. Swift入坑--block的定义

    typealias methodCompletionBlock = (String)->Void

  2. 理解virtual方法

    1.使用场景 virtual方法的使用场景:父类告诉子类,继承接口,修改实现,从而可以面向接口编程. non-virtual方法的使用场景:父类告诉子类,继承接口和实现,从而可以代码复用. 2.成员方 ...

  3. 画表格防OFFICE的功能

    http://files.cnblogs.com/xe2011/officetable.rar 画表格防OFFICE的功能

  4. c++ 操作注冊表

    1.       注冊表简单介绍 注冊表是为Windows NT和Windows95中全部32位硬件/驱动和32位应用程序设计的数据文件,用于存储系统和应用程序的设置信息.16位驱动在Winnt (W ...

  5. SharePoint 2013+ Sqlserver 2014 Kerberos 配置传奇, 最终的解决方案 验证。

    SharePoint 2013+ Sqlserver 2014 Kerberos 配置传奇. 1,安装数据库,我就不多说安装,客户一定要注意. 我将参照以下实施例和账户. 2,建立DNS,假设没有DN ...

  6. 用C++程序理解汉字的机内码表示

    汉字的编码是非常多刚開始学习的人不easy搞不明确的事情.最早的汉字字符集是GB2312-80,收入汉字6763个,符号715个,总计7478个字符,大陆普遍使用的简体字字符集.本文借助于一个能输出这 ...

  7. 小谈一下Java I/O

    java中的I/O操作宏观上来说就氛围两个种类,一个是字节流 ,一个是字符流,分别相应着一组类和接口 字节流: InputStream 输入流的基类 OutputStream 输出流的基类 对于字节流 ...

  8. 图片轮换cycle插件的运用

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. mysqldump备份7

    http://www.cnblogs.com/ivictor/p/5505307.html   对于MySQL的备份,可分为以下两种: 1. 冷备 2. 热备 其中,冷备,顾名思义,就是将数据库关掉, ...

  10. Tomcat配置域名访问

    在server.xml文件中的<Host>标签里面添加 <Alias>你的域名(比如:www.baidu.com)</Alias> <Context path ...