原文: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. 简单的谈一下.NET下的AOP

    AOP有着它优良的好处, 从一个层面上去关心一个问题, .NET对此有良好的支持.如果硬生生的看MSDN不理解里面的思想是有掉入深渊的感觉的. 为了理解AOP的机制, 我们从需求开始说起,实现AOP就 ...

  2. js url传值中文乱码之解决之道

    在websphere 中使用的是url=encodeURI(encodeURI(url)); //用了2次encodeURI 测试成功,第一次转换没有尝试, 处理方法一. js 程序代码:url=en ...

  3. 【48】认识template元编程

    1.TMP(template metaprogramming),模版元编程有两个效力:第一,它让某些事情更容易:第二,可将工作从运行期转移到编译期.

  4. 【转】placement new

    原文:http://www.cnblogs.com/wanghetao/archive/2011/11/21/2257403.html 1. placement new的含义placement new ...

  5. PHP函数ip2long转换IP时数值太大产生负数的解决办法

    有两种办法: 1. bindec( decbin($long))  利用bindec和decbin两个函数转换一次就没有问题了 我一直在用上面的方法,但是在升级到PHP7以后就不起作用了(因为最近只进 ...

  6. BZOJ 1143: [CTSC2008]祭祀river 最长反链

    1143: [CTSC2008]祭祀river Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline ...

  7. SpringMVC+Spring3+hibernate4 开发环境搭建以及一个开发实例教程

    刚刚接触了SpringMVC这个框架,因此有必要把它拿过来同hibernate.Spring框架进行集成和开发一个实例,在真正企业从头开发的项目中往往一个稳定的开发环境至关重要,开发一个项目选择什么样 ...

  8. python抓取伯乐在线的全部文章,对标题分词后存入mongodb中

    依赖包: 1.pymongo 2.jieba # -*- coding: utf-8 -*- """ @author: jiangfuqiang "" ...

  9. Mysql shell 控制台---mysqlsh

    原创 2016-07-12 杜亦舒 性能与架构 以前登录Mysql的控制台后,使用SQL语言来操作数据库,如 mysql> select * from tablename; Mysql 5.7. ...

  10. 如何在mac上創建txt文档

    文件編輯(Mac下的記事本),shift + command + T 進入純文字編輯模式!