之前你已经学习了怎样处理并发异常,在本节中你将学习怎样实现继承。

在面向对象的编程中,你能够使用继承来重用代码。接下来你将改动Instructor和Student类,让它们派生自Person基类,该基类包括instructor和student共同拥有的属性如LastName。你不须要加入或改动不论什么WEB页面,可是你须要改动某些代码,这些改动会自己主动反映在数据库中。

映射继承到数据库的选项

School 数据模型中的Instructor和Student类有几个同样的属性:

如果你希望通过共享Instructor和Student实体的属性来降低冗余的代码,或者你希望编写一个服务程序来格式化姓名而不必关心姓名来自instructor还是student。你能够创建一个含有这些共同拥有属性的Person基类,然后让Instructor和Student继承该基类,例如以下图所看到的:

在数据库中,这样的继承结构能够有多种表现形式。你能够创建一个同一时候含有student和instructor信息的Person数据库表,该表中的某些列仅适用于instructor(HireDate),某些仅仅适用于student(EnrollmentDate),另外的就可以适用于instructor也可适用于student(LastName, FirstName)。通常情况下,你应该创建一个标识列来指明每一行所代表的类型,比如,标识列能够使用"Instructor"来表示instructors ,"Student"来表示students。

这样的从单个数据库表生成实体继承结构的模式被称为每一个层次结构一个表(table-per-hierarchy)继承。

还有一种替代方法是使数据库看起来更像是继承结构,,比如,你能够仅仅在Person表中包括name字段,在Instructor 和Student表中分别包括各自的date字段。

这样的为每一个实体类都建立一个数据库表的模式被称为每种类型一个表(table per type)继承。

还有一种选择是将全部非抽象类型映射到单独的表中。类的全部属性包含继承的,都将映射到对应表中的列,这样的模式被称为每一个详细的类一个表(Table-per-Concrete Class )继承。假设你为Person,Student和Instructor类实现了TPC 继承,那么Student和Instructor表将与之前的没有什么不同。

在Entity Framework中TPC 和TPH继承模式通常比TPT继承模式具有更好地性能,由于TPT模式可能会产生复杂的连接查询。

本节将教你怎样实现TPH继承。TPH继承是Entity Framework默认的继承模式,所以你须要做的就是创建一个Person类,改动Instructor和Student类使其派生自Person类,将新的类加入到DbContext,并创建迁移。

创建Person类

打开Models目录,新建Person.cs类,并使用以下的方法替换

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
public abstract class Person
{
public int ID { get; set; } [Required]
[StringLength(50)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(50, 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;
}
}
}
}

让Student和Instructor类继承Person

打开Instructor.cs,让Instructor类继承Person类,并删除key 和name字段

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
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; }
}
}

打开Student.cs,做相同的改动

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
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; }
}
}

将Person实体类型加入到模型中

打开 SchoolContext.cs,为Person实体类型加入DbSet 属性

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

这就是 Entity Framework配置每一个层次结构一个表继承所须要做的改动,稍后你会看到当数据库被更新后,会有一个新建的Person数据表。

创建并更新迁移文件

在 Package Manager Console (PMC)窗体,输入例如以下命令

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: 128, 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");
}

上面的代码运行了下列数据库更新任务:

移除了Student数据库表的外键约束和索引

  • 将Instructor表重命名为Person并作例如以下改动来存储Student 数据
    • 为Student 加入了能够为nullable 的EnrollmentDate
    • 加入了标识列来指明该列是一个student 还是instructor
    • 因为student 没有hire date,所以设置HireDate可为nullable
    • 加入一个暂时字段用来更新指向student 的外键
  • 将Student表中的数据复制到Person表中,这样Student能够得到一个新的主键值
  • 修复了指向student的外键值
  • 重建了外键约束和索引,让它们指向Person表

假设你已经使用了GUID而不是将integer作为主键类型,student 的主键值将不会被更改,同一时候上面的几个步骤能够被省略。

再次执行update-database命令。

在生产环境中你须要对Down方法进行对应的改动以免你不得不回滚至前一个数据库版本号。在本例中不须要使用Down方法。

注意:当迁移数据或更改架构时,你可能会碰到其他的错误,假设你遇到了迁移错误却无法解决,你能够通过改动Web.config文件里的连接字符串或删除已存在的数据库的方法来继续本教程,当然最简单的方法是又一次命名Web.config中的数据库。例如以下所看到的将数据库名称改动为ContosoUniversity2

<add name="SchoolContext"
connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />

通过使用新建的不存在不论什么数据的数据库来进行迁移,update-database应该会被成功运行。

測试

执行项目,尝试訪问不同的页面,一切执行正常。

打开Server Explorer依次展开Data Connections\SchoolContext \Tables,你能够看到Student和Instructor表已经被Person表替换,打开Person表,你能够看到该表拥有 Student 和Instructor表的全部列。

在Person表上右键单击Show Table Data查看discriminator列

以下是新的School数据库架构

部署到Windows Azure

1.在Visual Studio中,在Solution Explorer上右键选择Publish

2.点击Publish

默认浏览器中会自己主动打开该网站

3.验证应用程序会否工作正常

当你第一次执行项目并訪问数据库时,Entity Framework会自己主动执行全部迁移中的Up方法来确保数据库架构和当前数据模型一致。

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

欢迎转载,请注明文章出处:http://blog.csdn.net/johnsonblog/article/details/39503915

项目源代码:https://github.com/johnsonz/MvcContosoUniversity

还大家一个健康的网络环境,从你我做起

THE END

MVC5 Entity Framework学习之实现继承的更多相关文章

  1. MVC5 Entity Framework学习

    MVC5 Entity Framework学习(1):创建Entity Framework数据模型 MVC5 Entity Framework学习(2):实现基本的CRUD功能 MVC5 Entity ...

  2. MVC5 Entity Framework学习之实现主要的CRUD功能

    在上一篇文章中,我们使用Entity Framework 和SQL Server LocalDB创建了一个MVC应用程序,并使用它来存储和显示数据.在这篇文章中,你将对由 MVC框架自己主动创建的CR ...

  3. MVC5 Entity Framework学习之Entity Framework高级功能(转)

    在之前的文章中,你已经学习了如何实现每个层次结构一个表继承.本节中你将学习使用Entity Framework Code First来开发ASP.NET web应用程序时可以利用的高级功能. 在本节中 ...

  4. MVC5 Entity Framework学习之Entity Framework高级功能

    在之前的文章中,你已经学习了怎样实现每一个层次结构一个表继承. 本节中你将学习使用Entity Framework Code First来开发ASP.NET web应用程序时能够利用的高级功能. 在本 ...

  5. MVC5 Entity Framework学习之创建复杂的数据模型

    目录(?)[-] 使用属性来自定义数据模型 DataType属性 StringLength属性 Column 属性 完成对Student实体的更改 Required 属性 Display 属性 Ful ...

  6. MVC5 Entity Framework学习参加排序、筛选和排序功能

    上一篇文章实现Student 基本的实体CRUD操作.本文将展示如何Students Index页添加排序.筛选和分页功能. 以下是排序完成时.经过筛选和分页功能截图,您可以在列标题点击排序. 1.为 ...

  7. Entity Framework 学习整理(分播客整理)

    MSDN: http://msdn.microsoft.com/en-us/data/aa937723 台湾博客: http://www.dotblogs.com.tw/yc421206/ http: ...

  8. Entity Framework 学习笔记(2)

    上期回顾:Entity Framework 学习笔记(1) Entity Framework最主要的东西,就是自己创建的.继承于DbContext的类: /// <summary> /// ...

  9. Entity Framework 学习

    Entity Framework 学习初级篇1--EF基本概况 Entity Framework 学习初级篇2--ObjectContext.ObjectQuery.ObjectStateEntry. ...

随机推荐

  1. The mell hall——坑爹

    The mell hall 题目描述 In HUST,there are always manystudents go to the mell hall at the same time as soo ...

  2. 【LaTeX排版】LaTeX论文排版&lt;三&gt;

    A picture is worth a thousand words(一图胜千言).图在论文中的重要性不言而喻,本文主要解说图的制作与插入. 1.图像的插入     图像能够分为两大类:位图和向量图 ...

  3. [WPF]不规则窗体的实现

    Microsoft Expression Design 4 导入做好的login.Png图片 调整美工板大小 导出,右边格式为XAML WPF 资源字典,实时效果为XAML效果 文件名login.xa ...

  4. error C2504: “CActiveXDocControl”: 基类没有定义

    这样的错误,通常,第一个文件失败: 1.相互头包括 2.头文件秩序 此错误是编译错误,和"inclued头文件"有关 问题描写叙述 有三个头文件AgentSDK.h.AA.h.BB ...

  5. bnu1066

    hnu1066 给我们一张图,问我们摧毁边使得s和t不连通有多少种方案, 方案与方案之间不能存在相同的摧毁目标. 这是一个神奇的题目. 这题可以转为求s与t的最短路,为什么呢? 因为方案与方案之间不能 ...

  6. CSS检测的高像素密度屏幕设备

    iPhone4尽管是640px解析度,但它的屏幕宽度(device-width)目前只有320px和iPhone3G相同.只是iPhone4S的像素密度2. 然后使用meta viewport什么时候 ...

  7. 【Android进阶】让程序运行效率更高的编程技巧总结

    1.在程序中若出现字符串连接的情况,请使用StringBuffer代替String,这样可以减少多次创建String以及垃圾回收所带来的内存消耗 2.尽量使用局部变量.调用方法时传递的参数以及调用中创 ...

  8. 在mac os下编译android -相关文章

    1. Mac OS X下编译Android源码 http://blog.csdn.net/bulreed/article/details/22783467 2.MAC OS 编译 Android源代码 ...

  9. Memcached FAQ

    这篇FAQ包含了大家普遍关心的问题.非常值得一看. 原文:http://blog.csdn.net/jarfield/archive/2009/07/05/4322953.aspx 最后更新时间 20 ...

  10. 在将 varchar 值 '2,7' 转换成数据类型 int 时失败

    消息 245,级别 16,状态 1,第 1 行在将 varchar 值 '2,7' 转换成数据类型 int 时失败. 原sql select  UserName from s_User  where ...