使用EF6和MVC5实现一个简单的选课系统--使用EF6实现继承(11/12)
2014-03-24 21:05
766 查看
先贴出来英文的,抽空翻译!
本讲源代码下载
The Contoso University sample web application demonstrates how to create ASP.NET MVC 5 applications using the Entity Framework 6 Code First and Visual Studio 2013. For information about the tutorial series, seethe
first tutorial in the series.
In the previous tutorial you handled concurrency exceptions. This tutorial will show you how to implement inheritance in the data model.
In object-oriented programming, you can use inheritance to
facilitate code
reuse. In this tutorial, you'll change the
so that they derive from a
such as
You won't add or change any web pages, but you'll change some of the code and those changes will be automatically reflected in the database.
The
in the
Suppose you want to eliminate the redundant code for the properties that are shared by the
Or you want to write a service that can format names without caring whether the name came from an instructor or a student. You could create a
class which contains only those shared properties, then make the
inherit from that base class, as shown in the following illustration:
There are several ways this inheritance structure could be represented in the database. You could have a
that includes information about both students and instructors in a single table. Some of the columns could apply only to instructors (
some only to students (
Typically, you'd have a discriminator column to indicate which type each row represents. For example, the discriminator
column might have "Instructor" for instructors and "Student" for students.
This pattern of generating an entity inheritance structure from a single database table is called table-per-hierarchy(TPH)
inheritance.
An alternative is to make the database look more like the inheritance structure. For example, you could have only the name fields in the
and have separate
with the date fields.
This pattern of making a database table for each entity class is called table per type (TPT) inheritance.
Yet another option is to map all non-abstract types to individual tables. All properties of a class, including inherited properties, map to columns of the corresponding table. This pattern is called Table-per-Concrete Class (TPC) inheritance. If you implemented
TPC inheritance for the
and
would look no different after implementing inheritance than they did before.
TPC and TPH inheritance patterns generally deliver better performance in the Entity Framework than TPT inheritance patterns, because TPT patterns can result in complex join queries.
This tutorial demonstrates how to implement TPH inheritance. TPH is the default inheritance pattern in the Entity Framework, so all you have to do is create a
change the
to derive from
and create a migration. (For information about how to implement the other inheritance patterns, see Mapping
the Table-Per-Type (TPT) Inheritance and Mapping
the Table-Per-Concrete Class (TPC) Inheritance in the MSDN Entity Framework documentation.)
In the Models folder, create Person.cs and
replace the template code with the following code:
In Instructor.cs, derive the
from the
code will look like the following example:
Make similar changes to Student.cs. The
will look like the following example:
In SchoolContext.cs, add a
for the
This is all that the Entity Framework needs in order to configure table-per-hierarchy inheritance. As you'll see, when the database is updated, it will have a
in place of the
In the Package Manager Console (PMC), enter the following command:
Run the
at this point because we have existing data that migrations doesn't know how to handle. You get an error message like the following one:
Could not drop object 'dbo.Instructor' because it is referenced by a FOREIGN KEY constraint.
Open Migrations\<timestamp>_Inheritance.cs and replace the
with the following code:
This code takes care of the following database update tasks:
Removes foreign key constraints and indexes that point to the Student table.
Renames the Instructor table as Person and makes changes needed for it to store Student data:
Adds nullable EnrollmentDate for students.
Adds Discriminator column to indicate whether a row is for a student or an instructor.
Makes HireDate nullable since student rows won't have hire dates.
Adds a temporary field that will be used to update foreign keys that point to students. When you copy students into the Person table they'll get new primary key values.
Copies data from the Student table into the Person table. This causes students to get assigned new primary key values.
Fixes foreign key values that point to students.
Re-creates foreign key constraints and indexes, now pointing them to the Person table.
(If you had used GUID instead of integer as the primary key type, the student primary key values wouldn't have to change, and several of these steps could have been omitted.)
Run the
(In a production system you would make corresponding changes to the Down method in case you ever had to use that to go back to the previous database version. For this tutorial you won't be using the Down method.)
Note: It's possible to get other errors when migrating data and making schema changes. If you get
migration errors you can't resolve, you can continue with the tutorial by changing the connection string in the Web.config file
or by deleting the database. The simplest approach is to rename the database in the Web.config file. For example, change
the database name to ContosoUniversity2 as shown in the following example:
With a new database, there is no data to migrate, and the
is much more likely to complete without errors. For instructions on how to delete the database, see How
to Drop a Database from Visual Studio 2012. If you take this approach in order to continue with the tutorial, skip the deployment step at the end of this tutorial or deploy to a new site and database. If you deploy an update to the same site you've been
deploying to already, EF will get the same error there when it runs migrations automatically. If you want to troubleshoot a migrations error, the best resource is one of the Entity Framework forums or StackOverflow.com.
Run the site and try various pages. Everything works the same as it did before.
In Server Explorer, expand Data
Connections\SchoolContext and then Tables, and you see that the Student andInstructor tables
have been replaced by a Person table. Expand the Person table
and you see that it has all of the columns that used to be in the Student and Instructor tables.
Right-click the Person table, and then click Show Table Data to see the discriminator column.
The following diagram illustrates the structure of the new School database:
This section requires you to have completed the optional Deploying the app to Windows Azure section in Part
3, Sorting, Filtering, and Paging of this tutorial series. If you had migrations errors that you resolved by deleting the database in your local project, skip this step; or create a new site and database, and deploy to the new environment.
In Visual Studio, right-click the project in Solution Explorer and select Publish from
the context menu.
Click Publish.
The Web app will open in your default browser.
Test the application to verify it's working.
The first time you run a page that accesses the database, the Entity Framework runs all of the migrations
required to bring the database up to date with the current data model.
You've implemented table-per-hierarchy inheritance for the
and
inheritance structures, see TPT
Inheritance Pattern and TPH
Inheritance Pattern on MSDN. In the next tutorial you'll see some ways to implement the repository and unit of work patterns.
Links to other Entity Framework resources can be found in the ASP.NET
Data Access - Recommended Resources.
本讲源代码下载
The Contoso University sample web application demonstrates how to create ASP.NET MVC 5 applications using the Entity Framework 6 Code First and Visual Studio 2013. For information about the tutorial series, seethe
first tutorial in the series.
In the previous tutorial you handled concurrency exceptions. This tutorial will show you how to implement inheritance in the data model.
In object-oriented programming, you can use inheritance to
facilitate code
reuse. In this tutorial, you'll change the
Instructorand
Studentclasses
so that they derive from a
Personbase class which contains properties
such as
LastNamethat are common to both instructors and students.
You won't add or change any web pages, but you'll change some of the code and those changes will be automatically reflected in the database.
Options for mapping inheritance to database tables
The Instructorand
Studentclasses
in the
Schooldata model have several properties that are identical:
Suppose you want to eliminate the redundant code for the properties that are shared by the
Instructorand
Studententities.
Or you want to write a service that can format names without caring whether the name came from an instructor or a student. You could create a
Personbase
class which contains only those shared properties, then make the
Instructorand
Studententities
inherit from that base class, as shown in the following illustration:
There are several ways this inheritance structure could be represented in the database. You could have a
Persontable
that includes information about both students and instructors in a single table. Some of the columns could apply only to instructors (
HireDate),
some only to students (
EnrollmentDate), some to both (
LastName,
FirstName).
Typically, you'd have a discriminator column to indicate which type each row represents. For example, the discriminator
column might have "Instructor" for instructors and "Student" for students.
This pattern of generating an entity inheritance structure from a single database table is called table-per-hierarchy(TPH)
inheritance.
An alternative is to make the database look more like the inheritance structure. For example, you could have only the name fields in the
Persontable
and have separate
Instructorand
Studenttables
with the date fields.
This pattern of making a database table for each entity class is called table per type (TPT) inheritance.
Yet another option is to map all non-abstract types to individual tables. All properties of a class, including inherited properties, map to columns of the corresponding table. This pattern is called Table-per-Concrete Class (TPC) inheritance. If you implemented
TPC inheritance for the
Person,
Student,
and
Instructorclasses as shown earlier, the
Studentand
Instructortables
would look no different after implementing inheritance than they did before.
TPC and TPH inheritance patterns generally deliver better performance in the Entity Framework than TPT inheritance patterns, because TPT patterns can result in complex join queries.
This tutorial demonstrates how to implement TPH inheritance. TPH is the default inheritance pattern in the Entity Framework, so all you have to do is create a
Personclass,
change the
Instructorand
Studentclasses
to derive from
Person, add the new class to the
DbContext,
and create a migration. (For information about how to implement the other inheritance patterns, see Mapping
the Table-Per-Type (TPT) Inheritance and Mapping
the Table-Per-Concrete Class (TPC) Inheritance in the MSDN Entity Framework documentation.)
Create the Person class
In the Models folder, create Person.cs andreplace the template code with the following code:
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; } } } }
Make Student and Instructor classes inherit from Person
In Instructor.cs, derive the Instructorclass
from the
Personclass and remove the key and name fields. The
code will look like the following example:
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; } } }
Make similar changes to Student.cs. The
Studentclass
will look like the following example:
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; } } }
Add the Person Entity Type to the Model
In SchoolContext.cs, add a DbSetproperty
for the
Personentity type:
public DbSet<Person> People { get; set; }
This is all that the Entity Framework needs in order to configure table-per-hierarchy inheritance. As you'll see, when the database is updated, it will have a
Persontable
in place of the
Studentand
Instructortables.
Create and Update a Migrations File
In the Package Manager Console (PMC), enter the following command:Add-Migration Inheritance
Run the
Update-Databasecommand in the PMC. The command will fail
at this point because we have existing data that migrations doesn't know how to handle. You get an error message like the following one:
Could not drop object 'dbo.Instructor' because it is referenced by a FOREIGN KEY constraint.
Open Migrations\<timestamp>_Inheritance.cs and replace the
Upmethod
with the following code:
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"); }
This code takes care of the following database update tasks:
Removes foreign key constraints and indexes that point to the Student table.
Renames the Instructor table as Person and makes changes needed for it to store Student data:
Adds nullable EnrollmentDate for students.
Adds Discriminator column to indicate whether a row is for a student or an instructor.
Makes HireDate nullable since student rows won't have hire dates.
Adds a temporary field that will be used to update foreign keys that point to students. When you copy students into the Person table they'll get new primary key values.
Copies data from the Student table into the Person table. This causes students to get assigned new primary key values.
Fixes foreign key values that point to students.
Re-creates foreign key constraints and indexes, now pointing them to the Person table.
(If you had used GUID instead of integer as the primary key type, the student primary key values wouldn't have to change, and several of these steps could have been omitted.)
Run the
update-databasecommand again.
(In a production system you would make corresponding changes to the Down method in case you ever had to use that to go back to the previous database version. For this tutorial you won't be using the Down method.)
Note: It's possible to get other errors when migrating data and making schema changes. If you get
migration errors you can't resolve, you can continue with the tutorial by changing the connection string in the Web.config file
or by deleting the database. The simplest approach is to rename the database in the Web.config file. For example, change
the database name to ContosoUniversity2 as shown in the following example:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
With a new database, there is no data to migrate, and the
update-databasecommand
is much more likely to complete without errors. For instructions on how to delete the database, see How
to Drop a Database from Visual Studio 2012. If you take this approach in order to continue with the tutorial, skip the deployment step at the end of this tutorial or deploy to a new site and database. If you deploy an update to the same site you've been
deploying to already, EF will get the same error there when it runs migrations automatically. If you want to troubleshoot a migrations error, the best resource is one of the Entity Framework forums or StackOverflow.com.
Testing
Run the site and try various pages. Everything works the same as it did before.In Server Explorer, expand Data
Connections\SchoolContext and then Tables, and you see that the Student andInstructor tables
have been replaced by a Person table. Expand the Person table
and you see that it has all of the columns that used to be in the Student and Instructor tables.
Right-click the Person table, and then click Show Table Data to see the discriminator column.
The following diagram illustrates the structure of the new School database:
Deploy to Windows Azure
This section requires you to have completed the optional Deploying the app to Windows Azure section in Part3, Sorting, Filtering, and Paging of this tutorial series. If you had migrations errors that you resolved by deleting the database in your local project, skip this step; or create a new site and database, and deploy to the new environment.
In Visual Studio, right-click the project in Solution Explorer and select Publish from
the context menu.
Click Publish.
The Web app will open in your default browser.
Test the application to verify it's working.
The first time you run a page that accesses the database, the Entity Framework runs all of the migrations
Upmethods
required to bring the database up to date with the current data model.
Summary
You've implemented table-per-hierarchy inheritance for the Person,
Student,
and
Instructorclasses. For more information about this and other
inheritance structures, see TPT
Inheritance Pattern and TPH
Inheritance Pattern on MSDN. In the next tutorial you'll see some ways to implement the repository and unit of work patterns.
Links to other Entity Framework resources can be found in the ASP.NET
Data Access - Recommended Resources.
相关文章推荐
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6读取相关数据(7/12)
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6更新相关数据(8/12)
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6异步编程模式和存储过程(9/12)
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6处理并发操作(10/12)
- 使用EF6和MVC5实现一个简单的选课系统--启航(1/12)
- 使用EF6和MVC5实现一个简单的选课系统--排序、过滤和分页(3/12)
- 使用EF6和MVC5实现一个简单的选课系统--EF6的弹性链接和命令拦截(4/12)
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6实现基本的GRUD功能(2/12)
- 使用EF6和MVC5实现一个简单的选课系统--EF6的代码优先迁移和部署(5/12)
- 使用EF6和MVC5实现一个简单的选课系统--EF6的高级用法(12/12)
- 使用Node.js + MongoDB实现一个简单的日志分析系统
- [11] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序实现继承
- 使用python实现一个简单的学生信息管理系统
- Python使用multiprocessing实现一个最简单的分布式作业调度系统
- 从零开始,搭建博客系统MVC5+EF6搭建框架(4)上,前后台页面布局页面实现,介绍使用的UI框架以及JS组件
- 一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站
- 使用ICE实现一个简单的文件系统
- 使用Servlet和JSP实现一个简单的Web聊天室系统
- php使用face++实现一个简单的人脸识别系统
- 使用MongoDB和JSP实现一个简单的购物车系统实例