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 eliminate redundant code. In this tutorial, you'll change the

Suppose you want to eliminate the redundant code for the properties that are shared by the

There are several ways this inheritance structure could be represented in the database. You could have a

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

This pattern of making a database table for each entity class is called table per type (TPT) inheritance.
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. You'll do that by performing the following steps:
Next, perform a global change (all files in the project) to change
In InstructorController.cs, delete the three occurrences of the following line of code:
In Solution Explorer, double-click School.sdf to open the database in Server Explorer. Expand School.sdf and then Tables, and you see that the Student and Instructor 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, plus the discriminator column.

The following diagram illustrates the structure of the new School database:

Table-per-hierarchy inheritance has now been implemented for the
In object-oriented programming, you can use inheritance to eliminate redundant code. In this tutorial, you'll change the
Instructor and Student classes so that they derive from a Person base class which contains properties such as LastName
that 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.Table-per-Hierarchy versus Table-per-Type Inheritance
In object-oriented programming, you can use inheritance to make it easier to work with related classes. For example, theInstructor and Student classes in the School data model share several properties, which results in redundant code:Suppose you want to eliminate the redundant code for the properties that are shared by the
Instructor and Student entities. You could create a Person base class which contains only those shared properties, then make the Instructor and Student entities 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
Person
table 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. (In this case , 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
Person table and have separate Instructor and Student tables with the date fields.This pattern of making a database table for each entity class is called table per type (TPT) inheritance.
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. You'll do that by performing the following steps:
- Create a
Personclass and change theInstructorandStudentclasses to derive fromPerson. - Add model-to-database mapping code to the database context class.
- Change
InstructorIDandStudentIDreferences throughout the project toPersonID.
Creating the Person Class
In the Models folder, create Person.cs and replace the existing code with the following code:using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public abstract class Person { [Key] public int PersonID { get; set; } [Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")] [Column("FirstName")] [Display(Name = "First Name")] [MaxLength(50)] public string FirstMidName { get; set; } public string FullName { get { return LastName + ", " + FirstMidName; } } } }In Instructor.cs, derive the
Instructor class from the Person class 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; namespace ContosoUniversity.Models { public class Instructor : Person { [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] [Required(ErrorMessage = "Hire date is required.")] [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
Student class will look like the following example:using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student : Person { [Required(ErrorMessage = "Enrollment date is required.")] [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] [Display(Name = "Enrollment Date")] public DateTime? EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } }
Adding the Person Entity Type to the Model
In SchoolContext.cs, add aDbSet property for the Person entity 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 re-created, it will have a
Person table in place of the Student and Instructor tables.Changing InstructorID and StudentID to PersonID
In SchoolContext.cs, in the Instructor-Course mapping statement, changeMapRightKey("InstructorID") to MapRightKey("PersonID"):modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("PersonID") .ToTable("CourseInstructor"));This change isn't required; it just changes the name of the InstructorID column in the many-to-many join table. If you left the name as InstructorID, the application would still work correctly.
Next, perform a global change (all files in the project) to change
InstructorID to PersonID and StudentID to PersonID. Make sure that this change is case-sensitive. (Note that this demonstrates a disadvantage of the classnameID
pattern for naming primary keys. If you had named primary keys ID
without prefixing the class name, no renaming would be necessary now.)Adjusting Primary Key Values in the Initializer
In SchoolInitializer.cs, the code currently assumes that primary key values forStudent and Instructor entities are numbered separately. This is still correct for Student entities (they'll still be 1 through 8), but Instructor
entities will now be 9 through 13 instead of 1 through 5, because the
block of code that adds instructors comes after the one that adds
students in the initializer class. Replace the code that seeds the Department and OfficeAssignment entity sets with the following code that uses the new ID values for instructors:var departments = new List<Department> { new Department { Name = "English", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"), PersonID = 9 }, new Department { Name = "Mathematics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"), PersonID = 10 }, new Department { Name = "Engineering", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"), PersonID = 11 }, new Department { Name = "Economics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"), PersonID = 12 } };
var officeAssignments = new List<OfficeAssignment> { new OfficeAssignment { PersonID = 9, Location = "Smith 17" }, new OfficeAssignment { PersonID = 10, Location = "Gowan 27" }, new OfficeAssignment { PersonID = 11, Location = "Thompson 304" }, };
Changing OfficeAssignment to Lazy Loading
The current version of the Entity Framework doesn't support eager loading for one-to-zero-or-one relationships when the navigation property is on the derived class of a TPH inheritance structure. This is the case with theOfficeAssignment property on the Instructor entity. To work around this, you'll remove the code you added earlier to perform eager loading on this property.In InstructorController.cs, delete the three occurrences of the following line of code:
.Include(i => i.OfficeAssignment)
Testing
Run the site and try various pages. Everything works the same as it did before.In Solution Explorer, double-click School.sdf to open the database in Server Explorer. Expand School.sdf and then Tables, and you see that the Student and Instructor 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, plus the discriminator column.
The following diagram illustrates the structure of the new School database:
Table-per-hierarchy inheritance has now been implemented for the
Person, Student, and Instructor classes.
No comments:
Post a Comment