Bagikan melalui


Tutorial: Menerapkan pewarisan - ASP.NET MVC dengan EF Core

Dalam tutorial sebelumnya, Anda menangani pengecualian konkurensi. Tutorial ini akan menunjukkan kepada Anda cara menerapkan pewarisan dalam model data.

Dalam pemrograman berorientasi objek, Anda dapat menggunakan warisan untuk memfasilitasi penggunaan kembali kode. Dalam tutorial ini, Anda akan mengubah Instructor kelas dan Student sehingga mereka berasal dari Person kelas dasar yang berisi properti seperti LastName yang umum bagi instruktur dan siswa. Anda tidak akan menambahkan atau mengubah halaman web apa pun, tetapi Anda akan mengubah beberapa kode dan perubahan tersebut akan secara otomatis tercermin dalam database.

Di tutorial ini, Anda akan:

  • Pewarisan peta ke database
  • Membuat kelas Orang
  • Memperbarui Instruktur dan Siswa
  • Menambahkan Orang ke model
  • Membuat dan memperbarui migrasi
  • Menguji implementasi

Prasyarat

Pewarisan peta ke database

Kelas Instructor dan Student dalam model data Sekolah memiliki beberapa properti yang identik:

Student and Instructor classes

Misalkan Anda ingin menghilangkan kode redundan untuk properti yang dibagikan oleh Instructor entitas dan Student . Atau Anda ingin menulis layanan yang dapat memformat nama tanpa peduli apakah nama tersebut berasal dari instruktur atau siswa. Anda dapat membuat Person kelas dasar yang hanya berisi properti bersama tersebut Instructor , lalu membuat kelas dan Student mewarisi dari kelas dasar tersebut, seperti yang ditunjukkan dalam ilustrasi berikut:

Student and Instructor classes deriving from Person class

Ada beberapa cara struktur warisan ini dapat diwakili dalam database. Anda dapat memiliki Person tabel yang menyertakan informasi tentang siswa dan instruktur dalam satu tabel. Beberapa kolom hanya dapat berlaku untuk instruktur (HireDate), beberapa hanya untuk siswa (EnrollmentDate), beberapa ke keduanya (LastName, FirstName). Biasanya, Anda akan memiliki kolom diskriminator untuk menunjukkan jenis mana yang diwakili setiap baris. Misalnya, kolom diskriminator mungkin memiliki "Instruktur" untuk instruktur dan "Siswa" untuk siswa.

Table-per-hierarchy example

Pola pembuatan struktur pewarisan entitas dari tabel database tunggal ini disebut warisan tabel per hierarki (TPH ).

Alternatifnya adalah membuat database terlihat lebih seperti struktur warisan. Misalnya, Anda hanya dapat memiliki bidang nama dalam Person tabel dan memiliki tabel dan Student terpisah Instructor dengan bidang tanggal.

Peringatan

Table-Per-Type (TPT) tidak didukung oleh EF Core 3.x, namun telah diimplementasikan dalam EF Core 5.0.

Table-per-type inheritance

Pola pembuatan tabel database untuk setiap kelas entitas ini disebut warisan table-per-type (TPT ).

Namun opsi lain adalah memetakan semua jenis non-abstrak ke tabel individual. Semua properti kelas, termasuk properti yang diwariskan, memetakan ke kolom tabel terkait. Pola ini disebut warisan Table-per-Concrete Class (TPC ). Jika Anda menerapkan pewarisan TPC untuk Personkelas , , Studentdan Instructor seperti yang ditunjukkan sebelumnya, Student tabel dan Instructor tidak akan terlihat berbeda setelah menerapkan pewarisan daripada yang mereka lakukan sebelumnya.

Pola pewarisan TPC dan TPH umumnya memberikan performa yang lebih baik daripada pola warisan TPT, karena pola TPT dapat mengakibatkan kueri gabungan yang kompleks.

Tutorial ini menunjukkan cara menerapkan warisan TPH. TPH adalah satu-satunya pola pewarisan yang didukung Inti Kerangka Kerja Entitas. Apa yang akan Anda lakukan adalah membuat Person kelas, mengubah Instructor kelas dan Student untuk berasal dari Person, menambahkan kelas baru ke DbContext, dan membuat migrasi.

Tip

Pertimbangkan untuk menyimpan salinan proyek sebelum membuat perubahan berikut. Kemudian jika Anda mengalami masalah dan perlu memulai kembali, akan lebih mudah untuk memulai dari proyek yang disimpan alih-alih membalikkan langkah-langkah yang dilakukan untuk tutorial ini atau kembali ke awal seluruh seri.

Membuat kelas Orang

Di folder Model, buat Person.cs dan ganti kode templat dengan kode berikut:

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

Memperbarui Instruktur dan Siswa

Dalam Instructor.cs, dapatkan kelas Instruktur dari kelas Orang dan hapus bidang kunci dan nama. Kode akan terlihat seperti contoh berikut:

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

Buat perubahan yang sama di 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 ICollection<Enrollment> Enrollments { get; set; }
    }
}

Menambahkan Orang ke model

Tambahkan jenis entitas Orang ke SchoolContext.cs. Baris baru disorot.

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

Ini semua yang dibutuhkan Entity Framework untuk mengonfigurasi warisan tabel per hierarki. Seperti yang akan Anda lihat, saat database diperbarui, database akan memiliki tabel Orang sebagai pengganti tabel Siswa dan Instruktur.

Membuat dan memperbarui migrasi

Simpan perubahan Anda dan bangun proyek. Kemudian buka jendela perintah di folder proyek dan masukkan perintah berikut:

dotnet ef migrations add Inheritance

Jangan jalankan perintah.database update Perintah tersebut akan mengakibatkan data hilang karena akan menghilangkan tabel Instruktur dan mengganti nama tabel Siswa menjadi Orang. Anda perlu menyediakan kode kustom untuk mempertahankan data yang ada.

Buka Migrations/<timestamp>_Inheritance.cs dan ganti Up metode dengan kode berikut:

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

Kode ini menangani tugas pembaruan database berikut:

  • Menghapus batasan dan indeks kunci asing yang menunjuk ke tabel Siswa.

  • Mengganti nama tabel Instruktur sebagai Orang dan membuat perubahan yang diperlukan untuk menyimpan data Siswa:

  • Menambahkan EnrollmentDate nullable untuk siswa.

  • Menambahkan kolom Diskriminator untuk menunjukkan apakah baris adalah untuk siswa atau instruktur.

  • Membuat HireDate nullable karena baris siswa tidak akan memiliki tanggal sewa.

  • Menambahkan bidang sementara yang akan digunakan untuk memperbarui kunci asing yang menunjuk ke siswa. Saat Anda menyalin siswa ke dalam tabel Orang, mereka akan mendapatkan nilai kunci utama baru.

  • Menyalin data dari tabel Siswa ke dalam tabel Orang. Hal ini menyebabkan siswa mendapatkan nilai kunci primer baru yang ditetapkan.

  • Memperbaiki nilai kunci asing yang menunjuk ke siswa.

  • Membuat kembali batasan dan indeks kunci asing, sekarang mengarahkannya ke tabel Orang.

(Jika Anda telah menggunakan GUID alih-alih bilangan bulat sebagai jenis kunci utama, nilai kunci primer siswa tidak perlu berubah, dan beberapa langkah ini dapat dihilangkan.)

Jalankan database update perintah:

dotnet ef database update

(Dalam sistem produksi Anda akan membuat perubahan yang sesuai dengan metode jika Down Anda harus menggunakannya untuk kembali ke versi database sebelumnya. Untuk tutorial ini, Anda tidak akan menggunakan metode . Down )

Catatan

Dimungkinkan untuk mendapatkan kesalahan lain saat membuat perubahan skema dalam database yang memiliki data yang ada. Jika Anda mendapatkan kesalahan migrasi yang tidak bisa Anda atasi, Anda bisa mengubah nama database di string koneksi atau menghapus database. Dengan database baru, tidak ada data untuk dimigrasikan, dan perintah update-database lebih mungkin diselesaikan tanpa kesalahan. Untuk menghapus database, gunakan SSOX atau jalankan database drop perintah CLI.

Menguji implementasi

Jalankan aplikasi dan coba berbagai halaman. Semuanya bekerja sama seperti sebelumnya.

Di SQL Server Object Explorer, perluas Data Koneksi ions/SchoolContext lalu Tabel, dan Anda melihat bahwa tabel Siswa dan Instruktur telah digantikan oleh tabel Orang. Buka perancang tabel Orang dan Anda melihat bahwa tabel tersebut memiliki semua kolom yang dulunya ada dalam tabel Siswa dan Instruktur.

Person table in SSOX

Klik kanan tabel Orang, lalu klik Perlihatkan Data Tabel untuk melihat kolom diskriminator.

Person table in SSOX - table data

Mendapatkan kode

Unduh atau lihat aplikasi yang telah selesai.

Sumber Daya Tambahan:

Untuk informasi selengkapnya tentang pewarisan di Entity Framework Core, lihat Warisan.

Langkah berikutnya

Di tutorial ini, Anda akan:

  • Warisan yang dipetakan ke database
  • Membuat kelas Orang
  • Instruktur dan Siswa yang Diperbarui
  • Menambahkan Orang ke model
  • Membuat dan memperbarui migrasi
  • Menguji implementasi

Lanjutkan ke tutorial berikutnya untuk mempelajari cara menangani berbagai skenario Kerangka Kerja Entitas yang relatif canggih.