原文: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. 在ubuntu上面配置nginx实现反向代理

    1.下载nginx    官网:http://nginx.org/en/download.html    直接在服务器上下载 wget http://nginx.org/download/nginx- ...

  2. <转>使用eclipse编译cocos2d-x示例项目,创建cocos2d-x android项目并部署到真机

    准备 今天将cocos2d-x的示例项目tests编译到android真机运行,以及如何创建cocos2d-x的android项目. 打开cocos2d-x的tests项目,路径为:D:\cocos2 ...

  3. [RxJS] just, return

    Sometime, we migth just want to return a object which wrap into Observable. You can use 'just' or  ' ...

  4. iOS开发——网络编程OC篇&Socket编程

    Socket编程 一.网络各个协议:TCP/IP.SOCKET.HTTP等 网络七层由下往上分别为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 其中物理层.数据链路层和网络层通常被称作 ...

  5. 浅谈Perl的类、包、模块与面对对象编程

    http://blog.chinaunix.net/uid-27464093-id-3308003.html Perl面向对象 Perl面向对象学习例子实例代码教程 - 在我们了解perl的面向对象的 ...

  6. istringstream

    编写程序,将来自一个文件中的行保存在一个vector<string>中,然后使用一个istringstream从vector读取数据成员,每次读取一个单词 #include <ios ...

  7. Android(java)学习笔记126:Android Studio中build.gradle简介

    1.首先我们直接上代码介绍: // Top-level build file where you can add configuration options common to all sub-pro ...

  8. 阅读《RobHess的SIFT源码分析:综述》笔记

    今天总算是机缘巧合的找到了照样一篇纲要性质的文章. 如是能早一些找到就好了.不过“在你认为为时已晚的时候,其实还为时未晚”倒是也能聊以自慰,不过不能经常这样迷惑自己,毕竟我需要开始跑了! 就照着这个大 ...

  9. nginx配置文件特殊字符说明

    开发过程中经常重复配置nginx.conf,对里面的特殊字符始终不太明白具体的意义,今天百度nginx配置看到一篇不错的文章,转载记录下来,以备不时之需. nginx rewrite 正则表达式匹配 ...

  10. RabbitMQ 原文译1.1--HelloWord

    本系列文章均来自官网原文,属于个人翻译,如有雷同,权当个人归档,忽喷. RabitMQ 是一个消息中间件,其实就是从消息生产者那里接受消息,然后发送给消息消费者.在这个传输过程中,可以定义一些缓存,持 ...