[渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序实现继承
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十一篇:为ASP.NET MVC应用程序实现继承
原文:Implementing Inheritance with the Entity Framework 6 in an ASP.NET MVC 5 Application
译文版权所有,谢绝全文转载——但您可以在您的网站上添加到该教程的链接。
在之前的教程中,您已经学习了如何处理并发异常。在本教程中,我们将介绍如何实现继承。
在面向对象的编程中,你可以使用继承以便重用代码。在本教程中,您将更改Instructor和Student类,使它们从包含姓名属性的Person基类派生。你无须改动任何WEB页面,但你的改动会自动反映在数据库中。
映射继承到数据库的选项
数据模型中的Instructor和Student类有几个相同的属性:
假设您想要通过共享教师和学生实体的属性来消除冗余的代码,或者您想要编写一个无需关心名称是否来自学生或教师的从而正确格式化姓名的服务。你可以创建一个包含这些共享属性的Person基类,然后使教师和学生实体的类从基类继承,如下图所示:
###
在数据库中,这种继承结构有几种表现形式。你可以创建一个Person数据表,包含教师和学生和学生信息的单个表,某些列可能仅适用于教师(雇佣日期),某些只适用于学生(注册日期),某些两者都要使用(姓、名)。通常情况下,你会有一个标识列,以指示每一行所代表的类型,例如,标识列可能使用"Instructor"来表示教师,"Student"来表示学生。
从单个数据库表生成实体继承结构的模式被称为每层一表继承模式。
替代方法是使用看起来更像继承结构的数据库,例如,你可以只在Person表中包含学生和教师共有的属性,将独有的属性放在各自单独的表中。
使每个实体类都建立一个数据库表的模式成为每类型一表继承。
但另一种选择是将所有非抽象类型映射到单个表。所有类别的属性,包括继承的,都将映射到相应表中的列。这种模式被称为每具体类一表继承。如果您实现了Person,Student和Instructor类的具体类一表继承,Student和Instructor数据表将和之前你看到的没有两样。
每具体类一表和每层一表在实体框架中通常会提供比每类型一表更好地性能,因为每类型一表可能会导致复杂的连接查询。
本教程将演示如何实现每层一表继承。每层一表是实体框架默认的继承模式。你所要做的就是创建一个Person类,修改Instructor和Student类派生自Person,将新的类添加到DbContext及创建迁移。
创建Person类
在Models文件夹中,使用下面的代码创建Person类:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
public abstract class Person
{
public int ID { get; set; }
[Required]
[Display(Name = "姓")]
[StringLength()]
public string LastName { get; set; } [Required]
[Column("FirstName")]
[Display(Name = "名")]
[StringLength()]
public string FirstMidName { get; set; } [Display(Name = "全名")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
}
使Student和Instructor类继承自Person
在Instructor类中,修改类从Person派生并删除姓名字段,如下面的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;namespace ContosoUniversity.Models
{
public class Instructor :Person
{
public int ID { get; set; } [DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",ApplyFormatInEditMode = true)]
[Display(Name = "聘用日期")]
public DateTime HireDate { get; set; } public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
同样也对Student类进行修改:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models
{
public class Student : Person
{
[Display(Name = "注册日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
向模型中添加Person实体类型
向SchoolContext.cs中添加一个Person实体的DbSet属性:
public DbSet<Person> People { get; set; }
就是在实体框架中实现继承做需要的全部修改。稍后您会看到数据库在更新后,会有一个新建的Person数据表。
创建及更新一个迁移文件
在软件包管理器控制台中,输入以下命令:
Add-Migration Inheritance
之后运行update-database命令,命令将失败。因为实体框架不知道如何对我们现有的数据进行迁移,错误消息类似下面这样:
打开Migrations\<时间戳>-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");
}
这段代码执行了下列数据库更新任务:
- 删除了指向学生数据表的外键约束和索引
- 重命名Instructor表为Person表并进行了修改:
- 为学生添加了可以为空的EnrollmentDate
- 添加了标识列,以指示行是否为学生或教师
- 使雇佣日期可以为空,因为学生的行不会有雇佣日期
- 添加一个临时字段用来更新指向学生的外键。当你将学生复制回Person表时他们会有一个新的主键值。
- 将数据从学生表复制到Person表,这会导致学生有一个新的主键值
- 修复了指向学生的外键值
- 重新创建外键约束和索引,现在它们指向Person表
(如果你使用了GUID而不是int作为主键类型,学生的主键值不会改变,上面的几个步骤可能被省略。)
再次运行update-database命令。
注意:您可以仍然得到一个错误,在进行迁移或架构更改时,如果迁移的错误无法解决,您可以通过更改web.config连接字符串或删除该数据库的方法来继续本教程,最简单的方法是重新命名数据库。
测试
运行应用程序,尝试各种操作,一切都正常运行。
在服务器资源管理器中,展开数据连接,展开SchoolContext的数据表,你会看到Person表已经替换了Student和Instructor表,打开Person表,你会看到之前的学生和教师的信息。
下面的关系图说明了新数据库的结构:
部署到Windows Azure
本章跳过……
总结
你现在实现了Person、Student和Instructor类的每层次一个表继承。有关其他继承结构的信息,请参阅TPH Inheritance Pattern和TPT Inheritance Pattern。在下一教程中,您将看到如何实现仓储和单元工作模式。
作者信息
Tom Dykstra - Tom Dykstra是微软Web平台及工具团队的高级程序员,作家。
[渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序实现继承的更多相关文章
- [转] 使用 MVC 5 的 EF6 Code First 入门 系列
译文:http://www.cnblogs.com/Bce-/category/573301.html 原文:http://www.asp.net/mvc/overview/getting-start ...
- 用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署
用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署 这是微软官方SignalR 2.0教程Getting Started with En ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:实现基本的CRUD功能
英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:排序、筛选和分页
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第三篇:排序.筛选和分页 原文:Sort ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第四篇:MVC程序中实体框架的连接恢复和 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序创建更复杂的数据模型
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第六篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序读取相关数据
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第七篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序更新相关数据
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第八篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用异步及存储过程
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第九篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序处理并发
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十篇:为ASP.NET MVC应用程序 ...
随机推荐
- IOS第16天(5,Quartz2D雪花)
*** #import "HMView.h" @interface HMView() { int count; } @property (nonatomic, assign) CG ...
- Asp.net DropDownList 自定义样式(想怎么改就怎么改!)
最近在做一个asp.net的项目,需要对默认的dropdownlist样式进行美化,固有的dropdownlist的小箭头实在让人无法接受,于是开始在百度,google 上下求索,天不负有心人,终于找 ...
- 【SIGGRAPH】用【有说服力的照片真实】技术实现最终幻想15的视觉特效
原文:西川善司 http://www.4gamer.net/games/075/G007535/20160726064/ 最终幻想15的演讲会场.相当大,听众非常多. 在本次计算机图形和 ...
- 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解
Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解 多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...
- python中多进程(multiprocessing)
一.multiprocessing中使用子进程概念 from multiprocessing import Process 可以通过Process来构造一个子进程 p = Process(target ...
- 【转】ArcGIS 创建切片缓存方法工具总结
ArcGIS 创建切片缓存方法工具总结 http://wenku.baidu.com/link?url=Bm8AkmcJBzfiyat9N_Me6vlfSHEDCC_D1qBk5IB4X4CIDeKI ...
- Juery On事件的 事件触发流程
使用On 给控件赋值事件的时候,你有没有觉得很神奇那,那是因为他事件处理流程比较特殊. on()函数并不是为当前jQuery对象匹配的元素绑定事件处理函数,而是为它们的后代元素中符合选择器select ...
- 【测试分析】HTSM模型
◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5508428.html 概述 HTSM全称Heuristic ...
- mysql数据库、表、字段、记录:增、删、改、查
/* 结构:数据库.表.字段.记录 操作:增删改查 */ -- 1.数据库:增删改查 create datebase if not exists jkxy; drop database if exis ...
- Hadoop:搭建hadoop集群
操作系统环境准备: 准备几台服务器(我这里是三台虚拟机): linux ubuntu 14.04 server x64(下载地址:http://releases.ubuntu.com/14.04.2/ ...