Bagikan melalui


Menerapkan Pewarisan dengan Kerangka Kerja Entitas dalam Aplikasi MVC ASP.NET (8 dari 10)

oleh Tom Dykstra

Aplikasi web sampel Contoso University menunjukkan cara membuat aplikasi ASP.NET MVC 4 menggunakan Entity Framework 5 Code First dan Visual Studio 2012. Untuk informasi tentang seri tutorial, lihat tutorial pertama dalam seri ini.

Catatan

Jika Anda mengalami masalah yang tidak dapat Anda atasi, unduh bab yang telah selesai dan coba reprodurasi masalah Anda. Anda umumnya dapat menemukan solusi untuk masalah dengan membandingkan kode Anda dengan kode yang telah selesai. Untuk beberapa kesalahan umum dan cara mengatasinya, lihat Kesalahan dan Solusi.

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 pewarisan untuk menghilangkan kode redundan. 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.

Tabel per Hierarki versus Warisan Tabel per Jenis

Dalam pemrograman berorientasi objek, Anda dapat menggunakan warisan untuk mempermudah bekerja dengan kelas terkait. Misalnya, Instructor kelas dan Student dalam School model data berbagi beberapa properti, yang menghasilkan kode redundan:

Cuplikan layar yang memperlihatkan kelas Siswa dan Instruktur dengan kode redundan disorot.

Misalkan Anda ingin menghilangkan kode redundan untuk properti yang dibagikan oleh Instructor entitas dan Student . Anda dapat membuat Person kelas dasar yang hanya berisi properti bersama tersebut Instructor , lalu membuat entitas dan Student mewarisi dari kelas dasar tersebut, seperti yang ditunjukkan dalam ilustrasi berikut:

Cuplikan layar yang memperlihatkan kelas Siswa dan Instruktur yang berasal dari kelas Orang.

Ada beberapa cara struktur pewarisan ini dapat diwakili dalam database. Anda bisa 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 untuk 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.

Cuplikan layar yang memperlihatkan struktur pewarisan dari kelas entitas Orang.

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

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

Cuplikan layar yang memperlihatkan tabel database Instruktur dan Siswa baru yang berasal dari kelas entitas Orang.

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

Pola pewarisan TPH umumnya memberikan performa yang lebih baik dalam Kerangka Kerja Entitas daripada pola pewarisan TPT, karena pola TPT dapat menghasilkan kueri gabungan yang kompleks. Tutorial ini menunjukkan cara menerapkan pewarisan TPH. Anda akan melakukannya dengan melakukan langkah-langkah berikut:

  • Buat Person kelas dan ubah Instructor kelas dan Student untuk berasal dari Person.
  • Tambahkan kode pemetaan model-ke-database ke kelas konteks database.
  • Ubah InstructorID dan StudentID referensi di seluruh proyek menjadi PersonID.

Membuat Kelas Orang

Catatan: Anda tidak akan dapat mengkompilasi proyek setelah membuat kelas di bawah ini hingga Anda memperbarui pengontrol yang menggunakan kelas ini.

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
   {
      [Key]
      public int PersonID { get; set; }

      [RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
      [StringLength(50, MinimumLength = 1)]
      [Display(Name = "Last Name")]
      public string LastName { get; set; }

      [Column("FirstName")]
      [Display(Name = "First Name")]
      [StringLength(50, MinimumLength = 2, ErrorMessage = "First name must be between 2 and 50 characters.")]
      public string FirstMidName { get; set; }

      public string FullName
      {
         get
         {
            return LastName + ", " + FirstMidName;
         }
      }
   }
}

Di Instructor.cs, dapatkan Instructor kelas dari Person kelas dan hapus bidang kunci dan nama. Kode akan terlihat seperti contoh berikut:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Instructor : Person
    {
        [DataType(DataType.Date)]
        [Display(Name = "Hire Date")]
        public DateTime HireDate { get; set; }

        public virtual ICollection<Course> Courses { get; set; }
        public virtual OfficeAssignment OfficeAssignment { get; set; }
    }
}

Buat perubahan serupa dengan Student.cs. Kelas Student akan terlihat seperti contoh berikut:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Student : Person
    {
        [DataType(DataType.Date)]
        [Display(Name = "Enrollment Date")]
        public DateTime EnrollmentDate { get; set; }

        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

Menambahkan Jenis Entitas Orang ke Model

Di SchoolContext.cs, tambahkan DbSet properti untuk Person jenis entitas:

public DbSet<Person> People { get; set; }

Ini semua yang dibutuhkan Entity Framework untuk mengonfigurasi warisan tabel per hierarki. Seperti yang akan Anda lihat, saat database dibuat ulang, database akan memiliki Person tabel sebagai pengganti Student tabel dan Instructor .

Mengubah InstructorID dan StudentID ke PersonID

Di SchoolContext.cs, dalam pernyataan pemetaan Instructor-Course, ubah MapRightKey("InstructorID") menjadi MapRightKey("PersonID"):

modelBuilder.Entity<Course>()
    .HasMany(c => c.Instructors).WithMany(i => i.Courses)
    .Map(t => t.MapLeftKey("CourseID")
    .MapRightKey("PersonID")
    .ToTable("CourseInstructor"));

Perubahan ini tidak diperlukan; itu hanya mengubah nama kolom InstrukturID dalam tabel gabungan banyak-ke-banyak. Jika Anda meninggalkan nama sebagai InstructorID, aplikasi akan tetap berfungsi dengan benar. Berikut adalah SchoolContext.cs yang telah selesai:

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.DAL
{
   public class SchoolContext : DbContext
   {
      public DbSet<Course> Courses { get; set; }
      public DbSet<Department> Departments { get; set; }
      public DbSet<Enrollment> Enrollments { get; set; }
      public DbSet<Instructor> Instructors { get; set; }
      public DbSet<Student> Students { get; set; }
      public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
      public DbSet<Person> People { get; set; }

      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
         modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

         modelBuilder.Entity<Course>()
             .HasMany(c => c.Instructors).WithMany(i => i.Courses)
             .Map(t => t.MapLeftKey("CourseID")
                 .MapRightKey("PersonID")
                 .ToTable("CourseInstructor"));
      }
   }
}

Selanjutnya Anda perlu mengubah InstructorID ke PersonID dan StudentID ke PersonID di seluruh proyek kecuali dalam file migrasi bertanda waktu di folder Migrasi . Untuk melakukannya, Anda hanya akan menemukan dan membuka file yang perlu diubah, lalu melakukan perubahan global pada file yang dibuka. Satu-satunya file di folder Migrasi yang harus Anda ubah adalah Migrations\Configuration.cs.

  1. Penting

    Mulailah dengan menutup semua file yang terbuka di Visual Studio.

  2. Klik Temukan dan Ganti -- Temukan semua File di menu Edit , lalu cari semua file dalam proyek yang berisi InstructorID.

    Cuplikan layar yang memperlihatkan jendela Temukan dan Ganti. Kotak centang Instruktur I D, Proyek Saat Ini, Cocokkan huruf besar/kecil dan Cocokkan seluruh kata, dan tombol Temukan Semua disorot.

  3. Buka setiap file di jendela Temukan Hasilkecuali<file migrasi time-stamp>_.cs di folder Migrasi , dengan mengklik dua kali satu baris untuk setiap file.

    Cuplikan layar yang memperlihatkan jendela Temukan Hasil. File migrasi stempel waktu dicoret dengan warna merah.

  4. Buka dialog Ganti dalam File dan ubah Lihat keSemua Dokumen yang Terbuka.

  5. Gunakan dialog Ganti dalam File untuk mengubah semua InstructorID menjadi PersonID.

    Cuplikan layar yang memperlihatkan jendela Temukan dan Ganti. Id Orang dimasukkan di bidang Ganti dengan teks.

  6. Temukan semua file dalam proyek yang berisi StudentID.

  7. Buka setiap file di jendela Temukan Hasilkecuali<file migrasi time-stamp>_*.cs di folder Migrasi , dengan mengklik dua kali satu baris untuk setiap file.

    Cuplikan layar yang memperlihatkan jendela Temukan Hasil. File migrasi stempel waktu dicoret.

  8. Buka dialog Ganti dalam File dan ubah Lihat keSemua Dokumen yang Terbuka.

  9. Gunakan dialog Ganti dalam File untuk mengubah semua StudentID menjadi PersonID.

    Cuplikan layar yang memperlihatkan jendela Temukan dan Ganti. Ganti di File, Semua Buka Dokumen, Cocokkan huruf besar/kecil dan kotak centang Cocokkan seluruh kata, dan tombol Ganti Semua disorot.

  10. Bangun proyek.

(Perhatikan bahwa ini menunjukkan kerugian pola classnameID untuk penamaan kunci primer. Jika Anda telah menamai ID kunci primer tanpa awalan nama kelas, tidak ada penggantian nama yang diperlukan sekarang.)

Membuat dan Memperbarui File Migrasi

Di Package Manager Console (PMC), masukkan perintah berikut:

Add-Migration Inheritance

Jalankan Update-Database perintah di PMC. Perintah akan gagal pada saat ini karena kami memiliki data yang ada yang tidak tahu cara menangani migrasi. Anda mendapatkan kesalahan berikut:

Pernyataan ALTER TABLE berkonflik dengan batasan FOREIGN KEY "FK_dbo. Department_dbo. Person_PersonID". Konflik terjadi dalam database "ContosoUniversity", tabel "dbo. Person", kolom 'PersonID'.

Buka Migrasi< >tanda waktu_Inheritance.cs dan ganti Up metode dengan kode berikut:

public override void Up()
{
    DropForeignKey("dbo.Department", "InstructorID", "dbo.Instructor");
    DropForeignKey("dbo.OfficeAssignment", "InstructorID", "dbo.Instructor");
    DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
    DropForeignKey("dbo.CourseInstructor", "InstructorID", "dbo.Instructor");
    DropIndex("dbo.Department", new[] { "InstructorID" });
    DropIndex("dbo.OfficeAssignment", new[] { "InstructorID" });
    DropIndex("dbo.Enrollment", new[] { "StudentID" });
    DropIndex("dbo.CourseInstructor", new[] { "InstructorID" });
    RenameColumn(table: "dbo.Department", name: "InstructorID", newName: "PersonID");
    RenameColumn(table: "dbo.OfficeAssignment", name: "InstructorID", newName: "PersonID");
    RenameColumn(table: "dbo.Enrollment", name: "StudentID", newName: "PersonID");
    RenameColumn(table: "dbo.CourseInstructor", name: "InstructorID", newName: "PersonID");
    CreateTable(
        "dbo.Person",
        c => new
            {
                PersonID = c.Int(nullable: false, identity: true),
                LastName = c.String(maxLength: 50),
                FirstName = c.String(maxLength: 50),
                HireDate = c.DateTime(),
                EnrollmentDate = c.DateTime(),
                Discriminator = c.String(nullable: false, maxLength: 128),
                OldId = c.Int(nullable: false)
            })
        .PrimaryKey(t => t.PersonID);

    // Copy existing Student and Instructor 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, StudentId AS OldId FROM dbo.Student");
    Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, HireDate, null AS EnrollmentDate, 'Instructor' AS Discriminator, InstructorId AS OldId FROM dbo.Instructor");

    // Fix up existing relationships to match new PK's.
    Sql("UPDATE dbo.Enrollment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Enrollment.PersonId AND Discriminator = 'Student')");
    Sql("UPDATE dbo.Department SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Department.PersonId AND Discriminator = 'Instructor')");
    Sql("UPDATE dbo.OfficeAssignment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = OfficeAssignment.PersonId AND Discriminator = 'Instructor')");
    Sql("UPDATE dbo.CourseInstructor SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = CourseInstructor.PersonId AND Discriminator = 'Instructor')");

    // Remove temporary key
    DropColumn("dbo.Person", "OldId");

    AddForeignKey("dbo.Department", "PersonID", "dbo.Person", "PersonID");
    AddForeignKey("dbo.OfficeAssignment", "PersonID", "dbo.Person", "PersonID");
    AddForeignKey("dbo.Enrollment", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
    AddForeignKey("dbo.CourseInstructor", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
    CreateIndex("dbo.Department", "PersonID");
    CreateIndex("dbo.OfficeAssignment", "PersonID");
    CreateIndex("dbo.Enrollment", "PersonID");
    CreateIndex("dbo.CourseInstructor", "PersonID");
    DropTable("dbo.Instructor");
    DropTable("dbo.Student");
}

Jalankan lagi perintah update-database.

Catatan

Dimungkinkan untuk mendapatkan kesalahan lain saat memigrasikan data dan membuat perubahan skema. Jika Anda mendapatkan kesalahan migrasi yang tidak dapat Anda atasi, Anda dapat melanjutkan tutorial dengan mengubah string koneksi dalam file Web.config atau menghapus database. Pendekatan paling sederhana adalah mengganti nama database dalam file Web.config . Misalnya, ubah nama database menjadi CU_test seperti yang diperlihatkan dalam contoh berikut:

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;
      Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\CU_Test.mdf" 
      providerName="System.Data.SqlClient" />

Dengan database baru, tidak ada data untuk dimigrasikan, dan update-database perintah jauh lebih mungkin diselesaikan tanpa kesalahan. Untuk instruksi tentang cara menghapus database, lihat Cara Menghapus Database dari Visual Studio 2012. Jika Anda mengambil pendekatan ini untuk melanjutkan tutorial, lewati langkah penyebaran di akhir tutorial ini, karena situs yang disebarkan akan mendapatkan kesalahan yang sama ketika menjalankan migrasi secara otomatis. Jika Anda ingin memecahkan masalah kesalahan migrasi, sumber daya terbaik adalah salah satu forum Kerangka Kerja Entitas atau StackOverflow.com.

Pengujian

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

Di Penjelajah Server, perluas SchoolContext lalu Tabel, dan Anda melihat bahwa tabel Pelajar dan Instruktur telah digantikan oleh tabel Orang . Perluas tabel Orang dan Anda melihat bahwa tabel tersebut memiliki semua kolom yang dulunya berada dalam tabel Siswa dan Instruktur .

Cuplikan layar yang memperlihatkan jendela Penjelajah Server. Tab Koneksi Data, Konteks Sekolah, dan Tabel diperluas untuk memperlihatkan tabel Orang.

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

Cuplikan layar yang memperlihatkan tabel Orang. Nama kolom Diskriminator disorot.

Diagram berikut mengilustrasikan struktur database Sekolah baru:

Cuplikan layar yang memperlihatkan diagram database Sekolah.

Ringkasan

Warisan tabel per hierarki sekarang telah diimplementasikan untuk Personkelas , Student, dan Instructor . Untuk informasi selengkapnya tentang ini dan struktur warisan lainnya, lihat Strategi Pemetaan Warisan di blog Morteza Manavi. Dalam tutorial berikutnya Anda akan melihat beberapa cara untuk mengimplementasikan repositori dan unit pola kerja.

Tautan ke sumber daya Kerangka Kerja Entitas lainnya dapat ditemukan di Peta Konten Akses Data ASP.NET.