Nguon: Microsoft Learn · .NET 8.0

Hướng dẫn: Triển khai Inheritance (kế thừa) - ASP.NET MVC với EF Core

Nguồn: Tutorial: Implement inheritance - ASP.NET MVC with EF Core

Trong hướng dẫn trước, bạn đã xử lý các ngoại lệ concurrency. Hướng dẫn này trình bày cách triển khai inheritance (kế thừa) trong mô hình dữ liệu.

Trong lập trình hướng đối tượng (object-oriented programming), bạn có thể sử dụng inheritance để tạo điều kiện tái sử dụng code. Trong hướng dẫn này, bạn sẽ thay đổi các lớp InstructorStudent để chúng kế thừa từ lớp cơ sở Person chứa các thuộc tính như LastName (họ) chung cho cả instructor và student. Bạn sẽ không thêm hoặc thay đổi bất kỳ trang web nào, nhưng sẽ thay đổi một số code và những thay đổi đó sẽ được phản ánh tự động trong cơ sở dữ liệu.

Trong hướng dẫn này, bạn sẽ:

Điều kiện tiên quyết

Ánh xạ Inheritance vào cơ sở dữ liệu

Các lớp InstructorStudent trong mô hình dữ liệu School có nhiều thuộc tính giống hệt nhau:

Giả sử bạn muốn loại bỏ code trùng lặp cho các thuộc tính được chia sẻ bởi các thực thể InstructorStudent. Hoặc bạn muốn viết một service (dịch vụ) có thể định dạng tên mà không cần quan tâm đến tên đến từ instructor hay student. Bạn có thể tạo một lớp cơ sở Person chỉ chứa những thuộc tính chia sẻ đó, sau đó làm cho các lớp InstructorStudent kế thừa từ lớp cơ sở đó.

Có nhiều cách để biểu diễn cấu trúc inheritance này trong cơ sở dữ liệu:

Hướng dẫn này minh họa cách triển khai TPH inheritance. TPH là pattern (mẫu) inheritance duy nhất mà Entity Framework Core hỗ trợ.

Tạo lớp Person

Trong thư mục Models, tạo Person.cs và thay thế code template (mẫu) bằng code sau:

csharp
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;
            }
        }
    }
}

Cập nhật Instructor và Student

Trong Instructor.cs, cho lớp Instructor kế thừa từ lớp Person và xóa các trường key và tên. Code sẽ trông như sau:

csharp
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 ICollection<CourseAssignment> CourseAssignments { get; set; }
        public OfficeAssignment OfficeAssignment { get; set; }
    }
}

Thực hiện tương tự trong Student.cs:

csharp
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 ICollection<Enrollment> Enrollments { get; set; }
    }
}

Thêm Person vào Model

Thêm kiểu thực thể Person vào SchoolContext.cs:

csharp
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
        public DbSet<Department> Departments { get; set; }
        public DbSet<Instructor> Instructors { get; set; }
        public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
        public DbSet<CourseAssignment> CourseAssignments { get; set; }
        public DbSet<Person> People { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
            modelBuilder.Entity<Department>().ToTable("Department");
            modelBuilder.Entity<Instructor>().ToTable("Instructor");
            modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
            modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
            modelBuilder.Entity<Person>().ToTable("Person");

            modelBuilder.Entity<CourseAssignment>()
                .HasKey(c => new { c.CourseID, c.InstructorID });
        }
    }
}

Đây là tất cả những gì Entity Framework cần để cấu hình TPH inheritance. Khi cơ sở dữ liệu được cập nhật, nó sẽ có bảng Person thay thế cho bảng Student và Instructor.

Tạo và cập nhật Migrations

Lưu các thay đổi và build (xây dựng) dự án. Sau đó mở cửa sổ lệnh trong thư mục dự án và nhập lệnh sau:

dotnetcli
dotnet ef migrations add Inheritance

Đừng chạy lệnh database update ngay. Lệnh đó sẽ dẫn đến mất dữ liệu vì nó sẽ xóa bảng Instructor và đổi tên bảng Student thành Person. Bạn cần cung cấp code tùy chỉnh để bảo tồn dữ liệu hiện có.

Mở Migrations/<timestamp>_Inheritance.cs và thay thế phương thức Up bằng code sau:

csharp
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropForeignKey(
        name: "FK_Enrollment_Student_StudentID",
        table: "Enrollment");

    migrationBuilder.DropIndex(name: "IX_Enrollment_StudentID", table: "Enrollment");

    migrationBuilder.RenameTable(name: "Instructor", newName: "Person");
    migrationBuilder.AddColumn<DateTime>(name: "EnrollmentDate", table: "Person", nullable: true);
    migrationBuilder.AddColumn<string>(name: "Discriminator", table: "Person", nullable: false, maxLength: 128, defaultValue: "Instructor");
    migrationBuilder.AlterColumn<DateTime>(name: "HireDate", table: "Person", nullable: true);
    migrationBuilder.AddColumn<int>(name: "OldId", table: "Person", nullable: true);

    // Copy existing Student data into new Person table.
    migrationBuilder.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.
    migrationBuilder.Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')");

    // Remove temporary key
    migrationBuilder.DropColumn(name: "OldID", table: "Person");

    migrationBuilder.DropTable(
        name: "Student");

    migrationBuilder.CreateIndex(
         name: "IX_Enrollment_StudentID",
         table: "Enrollment",
         column: "StudentID");

    migrationBuilder.AddForeignKey(
        name: "FK_Enrollment_Person_StudentID",
        table: "Enrollment",
        column: "StudentID",
        principalTable: "Person",
        principalColumn: "ID",
        onDelete: ReferentialAction.Cascade);
}

Code này xử lý các tác vụ cập nhật cơ sở dữ liệu sau:

Chạy lệnh database update:

dotnetcli
dotnet ef database update

Kiểm tra việc triển khai

Chạy ứng dụng và thử các trang khác nhau. Mọi thứ hoạt động giống như trước.

Trong SQL Server Object Explorer, mở rộng Data Connections/SchoolContext và sau đó Tables, bạn sẽ thấy rằng các bảng Student và Instructor đã được thay thế bằng bảng Person. Mở trình thiết kế bảng Person và bạn sẽ thấy nó có tất cả các cột từng có trong bảng Student và Instructor.

Nhấp chuột phải vào bảng Person, sau đó nhấp Show Table Data để xem cột discriminator.

Lấy code

Tải xuống hoặc xem ứng dụng hoàn chỉnh.